Merge "Fix registerSliceCallback to match api guidelines"
diff --git a/Android.bp b/Android.bp
index 641ce5f..9e9faf2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -230,6 +230,7 @@
         "core/java/android/os/ISchedulingPolicyService.aidl",
         "core/java/android/os/IStatsCompanionService.aidl",
         "core/java/android/os/IStatsManager.aidl",
+        "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
         "core/java/android/os/IThermalService.aidl",
         "core/java/android/os/IUpdateLock.aidl",
@@ -416,6 +417,8 @@
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
+        "media/java/android/media/IMediaSession2.aidl",
+        "media/java/android/media/IMediaSession2Callback.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
         ":libaudioclient_aidl",
         "media/java/android/media/IRecordingConfigDispatcher.aidl",
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 6975609..682885b 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -25,9 +25,12 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import android.content.res.ColorStateList;
+import android.graphics.Canvas;
 import android.graphics.Typeface;
 import android.text.Layout;
 import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -285,4 +288,157 @@
                     .build();
         }
     }
+
+    @Test
+    public void testDraw_FixedText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_Styled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_Styled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_NoStyled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_Styled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_Styled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_NoStyled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
 }
diff --git a/api/current.txt b/api/current.txt
index 31379cd..23a278e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6474,6 +6474,7 @@
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
     method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public boolean isPrintingEnabled();
     method public boolean isProfileOwnerApp(java.lang.String);
     method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isResetPasswordTokenActive(android.content.ComponentName);
@@ -6543,6 +6544,7 @@
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
+    method public void setPrintingEnabled(android.content.ComponentName, boolean);
     method public void setProfileEnabled(android.content.ComponentName);
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -13504,6 +13506,58 @@
     ctor public EmbossMaskFilter(float[], float, float, float);
   }
 
+  public final class ImageDecoder implements java.lang.AutoCloseable {
+    method public void close();
+    method public static android.graphics.ImageDecoder.Source createSource(android.content.ContentResolver, android.net.Uri);
+    method public static android.graphics.ImageDecoder.Source createSource(java.nio.ByteBuffer);
+    method public static android.graphics.ImageDecoder.Source createSource(java.io.File);
+    method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
+    method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException;
+    method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
+    method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source) throws java.io.IOException;
+    method public android.util.Size getSampledSize(int);
+    method public void setAllocator(int);
+    method public void setAsAlphaMask(boolean);
+    method public void setCrop(android.graphics.Rect);
+    method public void setMutable(boolean);
+    method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
+    method public void setPostProcessor(android.graphics.PostProcessor);
+    method public void setPreferRamOverQuality(boolean);
+    method public void setRequireUnpremultiplied(boolean);
+    method public void setResize(int, int);
+    method public void setResize(int);
+    field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
+    field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
+    field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
+    field public static final int ALLOCATOR_SOFTWARE = 1; // 0x1
+    field public static final int ERROR_SOURCE_ERROR = 3; // 0x3
+    field public static final int ERROR_SOURCE_EXCEPTION = 1; // 0x1
+    field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
+  }
+
+  public static abstract class ImageDecoder.Error implements java.lang.annotation.Annotation {
+  }
+
+  public static class ImageDecoder.ImageInfo {
+    method public java.lang.String getMimeType();
+    method public android.util.Size getSize();
+  }
+
+  public static class ImageDecoder.IncompleteException extends java.io.IOException {
+    ctor public ImageDecoder.IncompleteException();
+  }
+
+  public static abstract interface ImageDecoder.OnHeaderDecodedListener {
+    method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source);
+  }
+
+  public static abstract interface ImageDecoder.OnPartialImageListener {
+    method public abstract boolean onPartialImage(int, android.graphics.ImageDecoder.Source);
+  }
+
+  public static abstract class ImageDecoder.Source {
+  }
+
   public class ImageFormat {
     ctor public ImageFormat();
     method public static int getBitsPerPixel(int);
@@ -14074,6 +14128,10 @@
     ctor public PorterDuffXfermode(android.graphics.PorterDuff.Mode);
   }
 
+  public abstract interface PostProcessor {
+    method public abstract int onPostProcess(android.graphics.Canvas);
+  }
+
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
@@ -15574,8 +15632,10 @@
     method public <T> T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
     method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
     method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+    method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys();
     method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys();
     method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
+    method public java.util.List<java.lang.String> getPhysicalCameraIds();
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
@@ -15614,6 +15674,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
@@ -15654,6 +15715,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES;
@@ -15674,6 +15736,7 @@
   public abstract class CameraDevice implements java.lang.AutoCloseable {
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
+    method public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -15844,6 +15907,7 @@
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; // 0x4
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -15859,6 +15923,8 @@
     field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
     field public static final int LENS_STATE_MOVING = 1; // 0x1
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
+    field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0
+    field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
@@ -15868,6 +15934,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
@@ -15917,6 +15984,8 @@
     field public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; // 0x1
     field public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; // 0x0
     field public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // 0x1
+    field public static final int STATISTICS_OIS_DATA_MODE_OFF = 0; // 0x0
+    field public static final int STATISTICS_OIS_DATA_MODE_ON = 1; // 0x1
     field public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; // 0x1
     field public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; // 0x2
     field public static final int STATISTICS_SCENE_FLICKER_NONE = 0; // 0x0
@@ -15999,6 +16068,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_FACE_DETECT_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE;
@@ -16009,8 +16079,10 @@
     method public void addTarget(android.view.Surface);
     method public android.hardware.camera2.CaptureRequest build();
     method public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>);
+    method public <T> T getPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, java.lang.String);
     method public void removeTarget(android.view.Surface);
     method public <T> void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
+    method public <T> android.hardware.camera2.CaptureRequest.Builder setPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, T, java.lang.String);
     method public void setTag(java.lang.Object);
   }
 
@@ -16098,6 +16170,10 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<long[]> STATISTICS_OIS_TIMESTAMPS;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_X_SHIFTS;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_Y_SHIFTS;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA;
@@ -16206,6 +16282,7 @@
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
     method public void removeSurface(android.view.Surface);
+    method public void setPhysicalCameraId(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -21752,7 +21829,13 @@
     field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
     field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
     field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
+    field public static final int ENCODING_AAC_ELD = 15; // 0xf
+    field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
+    field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc
+    field public static final int ENCODING_AAC_LC = 10; // 0xa
+    field public static final int ENCODING_AAC_XHE = 16; // 0x10
     field public static final int ENCODING_AC3 = 5; // 0x5
+    field public static final int ENCODING_AC4 = 17; // 0x11
     field public static final int ENCODING_DEFAULT = 1; // 0x1
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
@@ -21760,6 +21843,7 @@
     field public static final int ENCODING_E_AC3 = 6; // 0x6
     field public static final int ENCODING_IEC61937 = 13; // 0xd
     field public static final int ENCODING_INVALID = 0; // 0x0
+    field public static final int ENCODING_MP3 = 9; // 0x9
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
     field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -21802,6 +21886,7 @@
     method public boolean isBluetoothScoOn();
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
+    method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
     method public boolean isSpeakerphoneOn();
     method public boolean isStreamMute(int);
     method public boolean isVolumeFixed();
@@ -22102,6 +22187,7 @@
     method public int reloadStaticData();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public void removeStreamEventCallback();
     method public int setAuxEffectSendLevel(float);
     method public int setBufferSizeInFrames(int);
     method public int setLoopPoints(int, int, int);
@@ -22115,6 +22201,7 @@
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
     method public deprecated int setStereoVolume(float, float);
+    method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
     method public int setVolume(float);
     method public void stop() throws java.lang.IllegalStateException;
     method public int write(byte[], int, int);
@@ -22150,6 +22237,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
     method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22165,6 +22253,12 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public static abstract class AudioTrack.StreamEventCallback {
+    method public void onStreamDataRequest(android.media.AudioTrack);
+    method public void onStreamPresentationEnd(android.media.AudioTrack);
+    method public void onTearDown(android.media.AudioTrack);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -26130,6 +26224,7 @@
     method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
+    method public byte[] getNetworkWatchlistConfigHash();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
     method public int getRestrictBackgroundStatus();
     method public boolean isActiveNetworkMetered();
@@ -27360,7 +27455,7 @@
     method public int addNetwork(android.net.wifi.WifiConfiguration);
     method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
     method public static int calculateSignalLevel(int, int);
-    method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
+    method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
     method public static int compareSignalLevel(int, int);
     method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
@@ -27393,7 +27488,7 @@
     method public boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startScan();
-    method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
+    method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
     method public int updateNetwork(android.net.wifi.WifiConfiguration);
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
@@ -27423,11 +27518,11 @@
     field public static final int WIFI_STATE_ENABLED = 3; // 0x3
     field public static final int WIFI_STATE_ENABLING = 2; // 0x2
     field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
-    field public static final int WPS_AUTH_FAILURE = 6; // 0x6
-    field public static final int WPS_OVERLAP_ERROR = 3; // 0x3
-    field public static final int WPS_TIMED_OUT = 7; // 0x7
-    field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
-    field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
+    field public static final deprecated int WPS_AUTH_FAILURE = 6; // 0x6
+    field public static final deprecated int WPS_OVERLAP_ERROR = 3; // 0x3
+    field public static final deprecated int WPS_TIMED_OUT = 7; // 0x7
+    field public static final deprecated int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
+    field public static final deprecated int WPS_WEP_PROHIBITED = 4; // 0x4
   }
 
   public static class WifiManager.LocalOnlyHotspotCallback {
@@ -27461,11 +27556,11 @@
     method public void setWorkSource(android.os.WorkSource);
   }
 
-  public static abstract class WifiManager.WpsCallback {
+  public static abstract deprecated class WifiManager.WpsCallback {
     ctor public WifiManager.WpsCallback();
-    method public abstract void onFailed(int);
-    method public abstract void onStarted(java.lang.String);
-    method public abstract void onSucceeded();
+    method public abstract deprecated void onFailed(int);
+    method public abstract deprecated void onStarted(java.lang.String);
+    method public abstract deprecated void onSucceeded();
   }
 
   public class WpsInfo implements android.os.Parcelable {
@@ -27907,6 +28002,29 @@
 
 package android.net.wifi.rtt {
 
+  public final class LocationCivic implements android.os.Parcelable {
+    method public int describeContents();
+    method public byte[] getData();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationCivic> CREATOR;
+  }
+
+  public final class LocationConfigurationInformation implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAltitude();
+    method public int getAltitudeType();
+    method public double getAltitudeUncertainty();
+    method public double getLatitude();
+    method public double getLatitudeUncertainty();
+    method public double getLongitude();
+    method public double getLongitudeUncertainty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ALTITUDE_IN_FLOORS = 2; // 0x2
+    field public static final int ALTITUDE_IN_METERS = 1; // 0x1
+    field public static final int ALTITUDE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationConfigurationInformation> CREATOR;
+  }
+
   public final class RangingRequest implements android.os.Parcelable {
     method public int describeContents();
     method public static int getMaxPeers();
@@ -27930,6 +28048,8 @@
     method public android.net.MacAddress getMacAddress();
     method public android.net.wifi.aware.PeerHandle getPeerHandle();
     method public long getRangingTimestampUs();
+    method public android.net.wifi.rtt.LocationCivic getReportedLocationCivic();
+    method public android.net.wifi.rtt.LocationConfigurationInformation getReportedLocationConfigurationInformation();
     method public int getRssi();
     method public int getStatus();
     method public void writeToParcel(android.os.Parcel, int);
@@ -31531,7 +31651,7 @@
   }
 
   public final class Debug {
-    method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
+    method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException;
     method public static deprecated void changeDebugPort(int);
     method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
     method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -34008,9 +34128,10 @@
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
     field public static final java.lang.String FEATURES = "features";
+    field public static final int FEATURES_ASSISTED_DIALING_USED = 16; // 0x10
     field public static final int FEATURES_HD_CALL = 4; // 0x4
     field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
-    field public static final int FEATURES_RTT = 16; // 0x10
+    field public static final int FEATURES_RTT = 32; // 0x20
     field public static final int FEATURES_VIDEO = 1; // 0x1
     field public static final int FEATURES_WIFI = 8; // 0x8
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
@@ -39858,6 +39979,7 @@
     field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
     field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_CONFERENCE = 1; // 0x1
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -40078,6 +40200,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
@@ -40500,6 +40623,7 @@
     field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+    field public static final java.lang.String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -40515,6 +40639,7 @@
     field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+    field public static final java.lang.String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
@@ -40527,6 +40652,18 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public final class TransformationInfo implements android.os.Parcelable {
+    ctor public TransformationInfo(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getOriginalNumber();
+    method public java.lang.String getTransformedNumber();
+    method public int getTransformedNumberCountryCallingCode();
+    method public java.lang.String getUserHomeCountryCode();
+    method public java.lang.String getUserRoamingCountryCode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.TransformationInfo> CREATOR;
+  }
+
   public class VideoProfile implements android.os.Parcelable {
     ctor public VideoProfile(int);
     ctor public VideoProfile(int, int);
@@ -40690,6 +40827,7 @@
     field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
     field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+    field public static final java.lang.String KEY_ASSISTED_DIALING_ENABLED_BOOL = "assisted_dialing_enabled_bool";
     field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
     field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
     field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
@@ -41435,6 +41573,8 @@
     method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method public int getAndroidCarrierIdForSubscription();
+    method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public deprecated android.telephony.CellLocation getCellLocation();
@@ -41472,8 +41612,6 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
-    method public int getSubscriptionCarrierId();
-    method public java.lang.String getSubscriptionCarrierName();
     method public java.lang.String getVisualVoicemailPackageName();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -41691,6 +41829,7 @@
     method public java.net.InetAddress getMmsProxy();
     method public java.net.URL getMmsc();
     method public java.lang.String getMvnoType();
+    method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
     method public int getPort();
@@ -41734,11 +41873,11 @@
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setId(int);
     method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
     method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
     method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
     method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPort(int);
@@ -43075,9 +43214,13 @@
     ctor public BulletSpan();
     ctor public BulletSpan(int);
     ctor public BulletSpan(int, int);
+    ctor public BulletSpan(int, int, int);
     ctor public BulletSpan(android.os.Parcel);
     method public int describeContents();
     method public void drawLeadingMargin(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, boolean, android.text.Layout);
+    method public int getBulletRadius();
+    method public int getColor();
+    method public int getGapWidth();
     method public int getLeadingMargin(boolean);
     method public int getSpanTypeId();
     method public void writeToParcel(android.os.Parcel, int);
@@ -50149,6 +50292,7 @@
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
     method public android.webkit.WebChromeClient getWebChromeClient();
+    method public static java.lang.ClassLoader getWebViewClassLoader();
     method public android.webkit.WebViewClient getWebViewClient();
     method public void goBack();
     method public void goBackOrForward(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 66b6d99..97f82e9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@
     field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
+    field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
     field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
     field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
     field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
@@ -131,6 +132,7 @@
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+    field public static final java.lang.String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
     field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
     field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
     field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
@@ -380,6 +382,7 @@
     method public java.lang.CharSequence getDeviceOwnerOrganizationName();
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
     method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
+    method public java.lang.CharSequence getPrintingDisabledReason();
     method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
     method public int getUserProvisioningState();
@@ -760,6 +763,7 @@
     field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
     field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
     field public static final java.lang.String STATS_MANAGER = "stats";
+    field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final java.lang.String VR_SERVICE = "vrmanager";
     field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
     field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -790,8 +794,6 @@
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
-    field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.intent.action.SIM_APPLICATION_STATE_CHANGED";
-    field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.intent.action.SIM_CARD_STATE_CHANGED";
     field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -3443,6 +3445,7 @@
     field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
     field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
+    field public static final java.lang.String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
     field public static final java.lang.String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
     field public static final java.lang.String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION";
     field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
@@ -3502,6 +3505,30 @@
     method public abstract void onResult(android.os.Bundle);
   }
 
+  public class SystemProperties {
+    method public static java.lang.String get(java.lang.String);
+    method public static java.lang.String get(java.lang.String, java.lang.String);
+    method public static boolean getBoolean(java.lang.String, boolean);
+    method public static int getInt(java.lang.String, int);
+    method public static long getLong(java.lang.String, long);
+  }
+
+  public class SystemUpdateManager {
+    method public android.os.Bundle retrieveSystemUpdateInfo();
+    method public void updateSystemUpdateInfo(android.os.PersistableBundle);
+    field public static final java.lang.String KEY_IS_SECURITY_UPDATE = "is_security_update";
+    field public static final java.lang.String KEY_STATUS = "status";
+    field public static final java.lang.String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+    field public static final java.lang.String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+    field public static final java.lang.String KEY_TITLE = "title";
+    field public static final int STATUS_IDLE = 1; // 0x1
+    field public static final int STATUS_IN_PROGRESS = 3; // 0x3
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+    field public static final int STATUS_WAITING_DOWNLOAD = 2; // 0x2
+    field public static final int STATUS_WAITING_INSTALL = 4; // 0x4
+    field public static final int STATUS_WAITING_REBOOT = 5; // 0x5
+  }
+
   public class UpdateEngine {
     ctor public UpdateEngine();
     method public void applyPayload(java.lang.String, long, long, java.lang.String[]);
@@ -4498,6 +4525,9 @@
     method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
     method public void toggleRadioOnOff();
     method public void updateServiceLocation();
+    field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -4968,6 +4998,7 @@
     method public abstract android.webkit.TracingController getTracingController();
     method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
     method public abstract android.webkit.WebStorage getWebStorage();
+    method public abstract java.lang.ClassLoader getWebViewClassLoader();
     method public abstract android.webkit.WebViewDatabase getWebViewDatabase(android.content.Context);
   }
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 01f4a84..a7daa3f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -136,7 +136,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 9d6d8a1..7a7a2f6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -127,6 +127,10 @@
     StatsdStats::getInstance().noteAtomLogged(
         event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC);
 
+    if (mMetricsManagers.empty()) {
+        return;
+    }
+
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
     if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ef99c9f..7a9588d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -85,6 +85,7 @@
         AppStartCancelChanged app_start_cancel_changed = 49;
         AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
         LmkEventOccurred lmk_event_occurred = 51;
+        PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -107,6 +108,8 @@
         CpuIdleTime cpu_idle_time = 10015;
         CpuActiveTime cpu_active_time = 10016;
         CpuClusterTime cpu_cluster_time = 10017;
+        DiskSpace disk_space = 10018;
+        SystemUptime system_uptime = 10019;
     }
 }
 
@@ -327,8 +330,9 @@
     optional string name = 2;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        FINISHED = 0;
+        STARTED = 1;
+        SCHEDULED = 2;
     }
     optional State state = 3;
 
@@ -937,6 +941,31 @@
 }
 
 /**
+ * Logs a picture-in-picture action
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+ *      frameworks/base/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+ */
+message PictureInPictureStateChanged {
+    optional int32 uid = 1;
+
+    optional string package_name = 2;
+
+    optional string class_name = 3;
+
+    // Picture-in-Picture action occurred, similar to
+    // frameworks/base/proto/src/metrics_constants.proto
+    enum State {
+        ENTERED = 1;
+        EXPANDED_TO_FULL_SCREEN = 2;
+        MINIMIZED = 3;
+        DISMISSED = 4;
+    }
+    optional State state = 4;
+}
+
+/**
  * Pulls bytes transferred via wifi (Sum of foreground and background usage).
  *
  * Pulled from:
@@ -1270,4 +1299,27 @@
     optional uint64 uid = 1;
     optional uint64 idx = 2;
     optional uint64 time_ms = 3;
+}
+
+/*
+ * Pulls free disk space, for data, system partition and temporary directory.
+ */
+message DiskSpace {
+    // available bytes in data partition
+    optional uint64 data_available_bytes = 1;
+    // available bytes in system partition
+    optional uint64 system_available_bytes = 2;
+    // available bytes in download cache or temp directories
+    optional uint64 temp_available_bytes = 3;
+}
+
+/*
+ * Pulls system up time.
+ */
+message SystemUptime {
+    // Milliseconds since the system was booted.
+    // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting
+    // for external input).
+    // It is not affected by clock scaling, idle, or other power saving mechanisms.
+    optional uint64 uptime_ms = 1;
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 79f1a5d..e06ae48 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -66,6 +66,15 @@
     mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
     mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
     mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
+    mPullers.insert({android::util::DISK_SPACE,
+                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
+    mPullers.insert({android::util::SYSTEM_UPTIME,
+                     make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
+    mPullers.insert(
+            {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+             make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
+    mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
+                     make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
 
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 6782f3f..34fa3c4 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -36,13 +36,13 @@
 using android::util::ProtoOutputStream;
 
 LogEvent::LogEvent(log_msg& msg) {
-    android_log_context context =
+    mContext =
             create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
     mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
     mLogUid = msg.entry_v4.uid;
-    init(context);
-    if (context) {
-        android_log_destroy(&context);
+    init(mContext);
+    if (mContext) {
+        android_log_destroy(&mContext);
     }
 }
 
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
index 7636268..5d43ef3 100644
--- a/cmds/statsd/src/logd/LogReader.cpp
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -98,7 +98,10 @@
 
             // Read a message
             err = android_logger_list_read(loggers, &msg);
-            if (err < 0) {
+            // err = 0 - no content, unexpected connection drop or EOF.
+            // err = +ive number - size of retrieved data from logger
+            // err = -ive number, OS supplied error _except_ for -EAGAIN
+            if (err <= 0) {
                 fprintf(stderr, "logcat read failure: %s\n", strerror(err));
                 break;
             }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ccb54f9..da9f728 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -339,4 +339,9 @@
      * Returns maximum number of users that can run simultaneously.
      */
     public abstract int getMaxRunningUsers();
+
+    /**
+     * Returns is the caller has the same uid as the Recents component
+     */
+    public abstract boolean isCallerRecents(int callingUid);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d1aacad..d378f22 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -75,6 +75,7 @@
     NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId);
     ParceledListSlice getNotificationChannelGroups(String pkg);
     boolean onlyHasDefaultChannel(String pkg, int uid);
+    ParceledListSlice getRecentNotifyingAppsForUser(int userId);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b469de5..c5a58f2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -458,7 +459,8 @@
      *
      * @see Context#startActivity(Intent, Bundle)
      */
-    public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
+    @NonNull
+    public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
         validateNotAppThread();
 
         synchronized (mSync) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75dc571..2e3b8af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6469,8 +6469,11 @@
                     ? super.mBigContentTitle
                     : mConversationTitle;
             boolean isOneToOne = TextUtils.isEmpty(conversationTitle);
+            CharSequence nameReplacement = null;
             if (hasOnlyWhiteSpaceSenders()) {
                 isOneToOne = true;
+                nameReplacement = conversationTitle;
+                conversationTitle = null;
             }
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
@@ -6489,6 +6492,8 @@
                     mBuilder.resolveContrastColor());
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
+            contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
+                    nameReplacement);
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
                     isOneToOne);
             contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 33277ea..fb8d101 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,6 +112,7 @@
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
+import android.os.ISystemUpdateManager;
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PowerManager;
@@ -119,6 +120,7 @@
 import android.os.RecoverySystem;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemUpdateManager;
 import android.os.SystemVibrator;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -485,6 +487,17 @@
                 return new StorageStatsManager(ctx, service);
             }});
 
+        registerService(Context.SYSTEM_UPDATE_SERVICE, SystemUpdateManager.class,
+                new CachedServiceFetcher<SystemUpdateManager>() {
+                    @Override
+                    public SystemUpdateManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.SYSTEM_UPDATE_SERVICE);
+                        ISystemUpdateManager service = ISystemUpdateManager.Stub.asInterface(b);
+                        return new SystemUpdateManager(service);
+                    }});
+
         registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
                 new CachedServiceFetcher<TelephonyManager>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d465e0d..7fccda8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9210,10 +9210,13 @@
     /**
      * Allows/disallows printing.
      *
+     * Called by a device owner or a profile owner.
+     * Device owner changes policy for all users. Profile owner can override it if present.
+     * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled whether printing should be allowed or not.
      * @throws SecurityException if {@code admin} is neither device, nor profile owner.
-     * @hide
      */
     public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
         try {
@@ -9224,10 +9227,12 @@
     }
 
     /**
-     * Returns whether printing is enabled for current user.
+     * Returns whether printing is enabled for this user.
+     *
+     * Always {@code false} if {@code FEATURE_PRINTING} is absent.
+     * Otherwise, {@code true} by default.
      *
      * @return {@code true} iff printing is enabled.
-     * @hide
      */
     public boolean isPrintingEnabled() {
         try {
@@ -9242,9 +9247,9 @@
      *
      * Used only by PrintService.
      * @return Localized error message.
-     * @throws SecurityException if caller is not system.
      * @hide
      */
+    @SystemApi
     public CharSequence getPrintingDisabledReason() {
         try {
             return mService.getPrintingDisabledReason();
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8ffacf5..bd4103f 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -89,7 +89,7 @@
  */
 public abstract class SliceProvider extends ContentProvider {
     /**
-     * This is the Android platform's MIME type for a slice: URI
+     * This is the Android platform's MIME type for a URI
      * containing a slice implemented through {@link SliceProvider}.
      */
     public static final String SLICE_TYPE = "vnd.android.slice";
@@ -299,7 +299,7 @@
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
         if (method.equals(METHOD_SLICE)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
             String callingPackage = getCallingPackage();
@@ -327,19 +327,19 @@
             }
             return b;
         } else if (method.equals(METHOD_PIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Only the system can pin/unpin slices");
             }
             handlePinSlice(uri);
         } else if (method.equals(METHOD_UNPIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Only the system can pin/unpin slices");
             }
             handleUnpinSlice(uri);
         } else if (method.equals(METHOD_GET_DESCENDANTS)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             Bundle b = new Bundle();
             b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
                     new ArrayList<>(handleGetDescendants(uri)));
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 90b514e..5576e86 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -131,6 +131,17 @@
         }
     }
 
+    /** @hide */
+    public Bucket querySummaryForDevice(NetworkTemplate template,
+            long startTime, long endTime) throws SecurityException, RemoteException {
+        Bucket bucket = null;
+        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        bucket = stats.getDeviceSummaryForNetwork();
+
+        stats.close();
+        return bucket;
+    }
+
     /**
      * Query network usage statistics summaries. Result is summarised data usage for the whole
      * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
@@ -163,12 +174,7 @@
             return null;
         }
 
-        Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
-        bucket = stats.getDeviceSummaryForNetwork();
-
-        stats.close();
-        return bucket;
+        return querySummaryForDevice(template, startTime, endTime);
     }
 
     /**
@@ -340,6 +346,37 @@
         return result;
     }
 
+    /** @hide */
+    public void registerUsageCallback(NetworkTemplate template, int networkType,
+            long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
+        checkNotNull(callback, "UsageCallback cannot be null");
+
+        final Looper looper;
+        if (handler == null) {
+            looper = Looper.myLooper();
+        } else {
+            looper = handler.getLooper();
+        }
+
+        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
+                template, thresholdBytes);
+        try {
+            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
+                    template.getSubscriberId(), callback);
+            callback.request = mService.registerUsageCallback(
+                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
+                    new Binder());
+            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
+
+            if (callback.request == null) {
+                Log.e(TAG, "Request from callback is null; should not happen");
+            }
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when registering callback");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Registers to receive notifications about data usage on specified networks.
      *
@@ -368,15 +405,7 @@
      */
     public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
             UsageCallback callback, @Nullable Handler handler) {
-        checkNotNull(callback, "UsageCallback cannot be null");
-
-        final Looper looper;
-        if (handler == null) {
-            looper = Looper.myLooper();
-        } else {
-            looper = handler.getLooper();
-        }
-
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
         if (DBG) {
             Log.d(TAG, "registerUsageCallback called with: {"
                 + " networkType=" + networkType
@@ -384,25 +413,7 @@
                 + " thresholdBytes=" + thresholdBytes
                 + " }");
         }
-
-        NetworkTemplate template = createTemplate(networkType, subscriberId);
-        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
-                template, thresholdBytes);
-        try {
-            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
-                    subscriberId, callback);
-            callback.request = mService.registerUsageCallback(
-                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
-                    new Binder());
-            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
-
-            if (callback.request == null) {
-                Log.e(TAG, "Request from callback is null; should not happen");
-            }
-        } catch (RemoteException e) {
-            if (DBG) Log.d(TAG, "Remote exception when registering callback");
-            throw e.rethrowFromSystemServer();
-        }
+        registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
     }
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 265f7c7..f69aab01 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3024,7 +3024,8 @@
             //@hide: INCIDENT_SERVICE,
             //@hide: STATS_COMPANION_SERVICE,
             COMPANION_DEVICE_SERVICE,
-            CROSS_PROFILE_APPS_SERVICE
+            CROSS_PROFILE_APPS_SERVICE,
+            //@hide: SYSTEM_UPDATE_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3242,6 +3243,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.SystemUpdateManager} for accessing the system update
+     * manager service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.view.WindowManager} for accessing the system's window
      * manager.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f05a4d1..ef1bcb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3525,74 +3525,6 @@
     public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
 
     /**
-     * Broadcast Action: The sim card state has changed.
-     * The intent will have the following extra values:</p>
-     * <dl>
-     *   <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt>
-     *   <dd>The sim card state. One of:
-     *     <dl>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_ABSENT}</dt>
-     *       <dd>SIM card not found</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_IO_ERROR}</dt>
-     *       <dd>SIM card IO error</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_RESTRICTED}</dt>
-     *       <dd>SIM card is restricted</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PRESENT}</dt>
-     *       <dd>SIM card is present</dd>
-     *     </dl>
-     *   </dd>
-     * </dl>
-     *
-     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
-     *
-     * <p class="note">The current state can also be queried using
-     * {@link android.telephony.TelephonyManager.getSimCardState()}
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SIM_CARD_STATE_CHANGED =
-            "android.intent.action.SIM_CARD_STATE_CHANGED";
-
-    /**
-     * Broadcast Action: The sim application state has changed.
-     * The intent will have the following extra values:</p>
-     * <dl>
-     *   <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt>
-     *   <dd>The sim application state. One of:
-     *     <dl>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NOT_READY}</dt>
-     *       <dd>SIM card applications not ready</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED}</dt>
-     *       <dd>SIM card PIN locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED}</dt>
-     *       <dd>SIM card PUK locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NETWORK_LOCKED}</dt>
-     *       <dd>SIM card network locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED}</dt>
-     *       <dd>SIM card permanently disabled due to PUK failures</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_LOADED}</dt>
-     *       <dd>SIM card data loaded</dd>
-     *     </dl>
-     *   </dd>
-     * </dl>
-     *
-     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
-     *
-     * <p class="note">The current state can also be queried using
-     * {@link android.telephony.TelephonyManager.getSimApplicationState()}
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
-            "android.intent.action.SIM_APPLICATION_STATE_CHANGED";
-
-    /**
      * Broadcast Action: indicate that the phone service state has changed.
      * The intent will have the following extra values:</p>
      * <p>
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index c5f1c85..5d10b88 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -107,8 +107,8 @@
      * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists.
      * If it does it adds the pair to the returned map.
      *
-     * Note that this method will do a strict
-     * matching based on the extension ('foo.dm' will only match 'foo.apk').
+     * Note that this method will do a loose
+     * matching based on the extension ('foo.dm' will match 'foo.apk' or 'foo').
      *
      * This should only be used for code paths extracted from a package structure after the naming
      * was enforced in the installer.
@@ -118,7 +118,7 @@
         ArrayMap<String, String> result = new ArrayMap<>();
         for (int i = codePaths.size() - 1; i >= 0; i--) {
             String codePath = codePaths.get(i);
-            String dexMetadataPath = buildDexMetadataPathForApk(codePath);
+            String dexMetadataPath = buildDexMetadataPathForFile(new File(codePath));
 
             if (Files.exists(Paths.get(dexMetadataPath))) {
                 result.put(codePath, dexMetadataPath);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1201ef4..96d043c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,9 +22,11 @@
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
 import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.TypeReference;
 import android.util.Rational;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -171,6 +173,7 @@
     private List<CameraCharacteristics.Key<?>> mKeys;
     private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
     private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
+    private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
     private List<CaptureResult.Key<?>> mAvailableResultKeys;
 
     /**
@@ -314,6 +317,45 @@
     }
 
     /**
+     * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
+     * be overriden for physical devices backing a logical multi-camera.</p>
+     *
+     * <p>This is a subset of android.request.availableRequestKeys which contains a list
+     * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+     * The respective value of such request key can be obtained by calling
+     * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+     * individual physical device requests must be built via
+     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+     * Such extended capture requests can be passed only to
+     * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+     * not to {@link CameraCaptureSession#setRepeatingRequest } or
+     * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+     *
+     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * a {@code UnsupportedOperationException}.</p>
+     *
+     * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
+     *
+     * @return List of keys that can be overriden in individual physical device requests.
+     * In case the camera device doesn't support such keys the list can be null.
+     */
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
+        if (mAvailableSessionKeys == null) {
+            Object crKey = CaptureRequest.Key.class;
+            Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+            int[] filterTags = get(REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
+            if (filterTags == null) {
+                return null;
+            }
+            mAvailablePhysicalRequestKeys =
+                    getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+        }
+        return mAvailablePhysicalRequestKeys;
+    }
+
+    /**
      * Returns the list of keys supported by this {@link CameraDevice} for querying
      * with a {@link CaptureRequest}.
      *
@@ -407,6 +449,47 @@
         return Collections.unmodifiableList(staticKeyList);
     }
 
+    /**
+     * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+     * made up of.
+     *
+     * <p>A camera device is a logical camera if it has
+     * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
+     * doesn't have the capability, the return value will be an empty list. </p>
+     *
+     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * a {@code UnsupportedOperationException}.</p>
+     *
+     * <p>Each physical camera id is only listed once in the list. The order of the keys
+     * is undefined.</p>
+     *
+     * @return List of physical camera ids for this logical camera device.
+     */
+    @NonNull
+    public List<String> getPhysicalCameraIds() {
+        int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
+        if (availableCapabilities == null) {
+            throw new AssertionError("android.request.availableCapabilities must be non-null "
+                        + "in the characteristics");
+        }
+
+        if (!ArrayUtils.contains(availableCapabilities,
+                REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+            return Collections.emptyList();
+        }
+        byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+        String physicalCamIdString = null;
+        try {
+            physicalCamIdString = new String(physicalCamIds, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+        }
+        String[] physicalCameraIdList = physicalCamIdString.split("\0");
+
+        return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+    }
+
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The key entries below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
@@ -1579,6 +1662,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1594,6 +1678,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
      * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1701,6 +1786,30 @@
             new Key<int[]>("android.request.availableSessionKeys", int[].class);
 
     /**
+     * <p>A subset of the available request keys that can be overriden for
+     * physical devices backing a logical multi-camera.</p>
+     * <p>This is a subset of android.request.availableRequestKeys which contains a list
+     * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+     * The respective value of such request key can be obtained by calling
+     * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+     * individual physical device requests must be built via
+     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+     * Such extended capture requests can be passed only to
+     * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+     * not to {@link CameraCaptureSession#setRepeatingRequest } or
+     * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =
+            new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
+
+    /**
      * <p>The list of image formats that are supported by this
      * camera device for output streams.</p>
      * <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -2870,6 +2979,21 @@
             new Key<int[]>("android.statistics.info.availableLensShadingMapModes", int[].class);
 
     /**
+     * <p>List of OIS data output modes for {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode} that
+     * are supported by this camera device.</p>
+     * <p>If no OIS data output is available for this camera device, this key will
+     * contain only OFF.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES =
+            new Key<int[]>("android.statistics.info.availableOisDataModes", int[].class);
+
+    /**
      * <p>Maximum number of supported points in the
      * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>If the actual number of points provided by the application (in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*) is
@@ -2978,6 +3102,7 @@
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li>
+     *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -2991,6 +3116,7 @@
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3
+     * @see #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
      */
     @PublicKey
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
@@ -3167,6 +3293,54 @@
     public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE =
             new Key<Boolean>("android.depth.depthIsExclusive", boolean.class);
 
+    /**
+     * <p>String containing the ids of the underlying physical cameras.</p>
+     * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
+     * The null terminator for physical camera id must be preserved so that the whole string
+     * can be tokenized using '\0' to generate list of physical camera ids.</p>
+     * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the
+     * value of this tag will be ['2', '\0', '3', '\0'].</p>
+     * <p>The number of physical camera ids must be no less than 2.</p>
+     * <p><b>Units</b>: UTF-8 null-terminated string</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<byte[]> LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =
+            new Key<byte[]>("android.logicalMultiCamera.physicalIds", byte[].class);
+
+    /**
+     * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
+     * <p>The accuracy of the frame timestamp synchronization determines the physical cameras'
+     * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
+     * the physical camera sensors usually run in master-slave mode so that their shutter
+     * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
+     * master-master mode, and there could be offset between their start of exposure.</p>
+     * <p>In both cases, all images generated for a particular capture request still carry the same
+     * timestamps, so that they can be used to look up the matching frame number and
+     * onCaptureStarted callback.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE APPROXIMATE}</li>
+     *   <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED CALIBRATED}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE
+     * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED
+     */
+    @PublicKey
+    public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
+            new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 639795a..40ee834 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -31,6 +31,7 @@
 import android.view.Surface;
 
 import java.util.List;
+import java.util.Set;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -910,6 +911,47 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
+     * initialized with template for a target use case. This methods allows
+     * clients to pass physical camera ids which can be used to customize the
+     * request for a specific physical camera. The settings are chosen
+     * to be the best options for the specific logical camera device. If
+     * additional physical camera ids are passed, then they will also use the
+     * same settings template. Requests containing individual physical camera
+     * settings can be passed only to {@link CameraCaptureSession#capture} or
+     * {@link CameraCaptureSession#captureBurst} and not to
+     * {@link CameraCaptureSession#setRepeatingRequest} or
+     * {@link CameraCaptureSession#setRepeatingBurst}</p>
+     *
+     * @param templateType An enumeration selecting the use case for this request. Not all template
+     * types are supported on every device. See the documentation for each template type for
+     * details.
+     * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+     *                            the request for a specific physical camera.
+     * @return a builder for a capture request, initialized with default
+     * settings for that template, and no output streams
+     *
+     * @throws IllegalArgumentException if the templateType is not supported by
+     * this device, or one of the physical id arguments matches with logical camera id.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #TEMPLATE_PREVIEW
+     * @see #TEMPLATE_RECORD
+     * @see #TEMPLATE_STILL_CAPTURE
+     * @see #TEMPLATE_VIDEO_SNAPSHOT
+     * @see #TEMPLATE_MANUAL
+     * @see CaptureRequest.Builder#setKey
+     * @see CaptureRequest.Builder#getKey
+     */
+    @NonNull
+    public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
+            Set<String> physicalCameraIdSet) throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
      * {@link TotalCaptureResult}.
      *
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 90bf896..a2bc91e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@
                 return;
             }
 
-            Integer oldStatus = mDeviceStatus.put(id, status);
+            Integer oldStatus;
+            if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+                oldStatus = mDeviceStatus.remove(id);
+            } else {
+                oldStatus = mDeviceStatus.put(id, status);
+            }
 
             if (oldStatus != null && oldStatus == status) {
                 if (DEBUG) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 2294ec5..e7c8961 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -845,6 +845,53 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
 
+    /**
+     * <p>The camera device is a logical camera backed by two or more physical cameras that are
+     * also exposed to the application.</p>
+     * <p>This capability requires the camera device to support the following:</p>
+     * <ul>
+     * <li>This camera device must list the following static metadata entries in {@link android.hardware.camera2.CameraCharacteristics }:<ul>
+     * <li>android.logicalMultiCamera.physicalIds</li>
+     * <li>{@link CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE android.logicalMultiCamera.sensorSyncType}</li>
+     * </ul>
+     * </li>
+     * <li>The underlying physical cameras' static metadata must list the following entries,
+     *   so that the application can correlate pixels from the physical streams:<ul>
+     * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+     * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
+     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * </ul>
+     * </li>
+     * <li>The logical camera device must be LIMITED or higher device.</li>
+     * </ul>
+     * <p>Both the logical camera device and its underlying physical devices support the
+     * mandatory stream combinations required for their device levels.</p>
+     * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
+     * <ul>
+     * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+     *   or raw stream with two physical streams of the same size and format, each from a
+     *   separate physical camera, given that the size and format are supported by both
+     *   physical cameras.</li>
+     * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
+     *   advertise RAW capability, but the underlying physical cameras do. This is usually
+     *   the case when the physical cameras have different sensor sizes.</li>
+     * </ul>
+     * <p>Using physical streams in place of a logical stream of the same size and format will
+     * not slow down the frame rate of the capture, as long as the minimum frame duration
+     * of the physical and logical streams are the same.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1134,6 +1181,38 @@
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3;
 
+    /**
+     * <p>This camera device is backed by an external camera connected to this Android device.</p>
+     * <p>The device has capability identical to a LIMITED level device, with the following
+     * exceptions:</p>
+     * <ul>
+     * <li>The device may not report lens/sensor related information such as<ul>
+     * <li>{@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}</li>
+     * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li>
+     * <li>{@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew}</li>
+     * </ul>
+     * </li>
+     * <li>The device will report 0 for {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}</li>
+     * <li>The device has less guarantee on stable framerate, as the framerate partly depends
+     *   on the external camera being used.</li>
+     * </ul>
+     *
+     * @see CaptureRequest#LENS_FOCAL_LENGTH
+     * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE
+     * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+     * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+     * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+     * @see CameraCharacteristics#SENSOR_ORIENTATION
+     * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4;
+
     //
     // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
     //
@@ -1160,6 +1239,26 @@
     public static final int SYNC_MAX_LATENCY_UNKNOWN = -1;
 
     //
+    // Enumeration values for CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+    //
+
+    /**
+     * <p>A software mechanism is used to synchronize between the physical cameras. As a result,
+     * the timestamp of an image from a physical stream is only an approximation of the
+     * image sensor start-of-exposure time.</p>
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     */
+    public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0;
+
+    /**
+     * <p>The camera device supports frame timestamp synchronization at the hardware level,
+     * and the timestamp of a physical stream image accurately reflects its
+     * start-of-exposure time.</p>
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     */
+    public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1;
+
+    //
     // Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE
     //
 
@@ -2566,6 +2665,22 @@
     public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1;
 
     //
+    // Enumeration values for CaptureRequest#STATISTICS_OIS_DATA_MODE
+    //
+
+    /**
+     * <p>Do not include OIS data in the capture result.</p>
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    public static final int STATISTICS_OIS_DATA_MODE_OFF = 0;
+
+    /**
+     * <p>Include OIS data in the capture result.</p>
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    public static final int STATISTICS_OIS_DATA_MODE_ON = 1;
+
+    //
     // Enumeration values for CaptureRequest#TONEMAP_MODE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ce75fa52..481b764 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -33,9 +33,11 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-
+import java.util.Set;
 
 /**
  * <p>An immutable package of settings and outputs needed to capture a single
@@ -219,7 +221,11 @@
 
     private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>();
 
-    private final CameraMetadataNative mSettings;
+    private String mLogicalCameraId;
+    private CameraMetadataNative mLogicalCameraSettings;
+    private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings =
+            new HashMap<String, CameraMetadataNative>();
+
     private boolean mIsReprocess;
     // If this request is part of constrained high speed request list that was created by
     // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
@@ -236,8 +242,6 @@
      * Used by Binder to unparcel this object only.
      */
     private CaptureRequest() {
-        mSettings = new CameraMetadataNative();
-        setNativeInstance(mSettings);
         mIsReprocess = false;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
     }
@@ -249,8 +253,14 @@
      */
     @SuppressWarnings("unchecked")
     private CaptureRequest(CaptureRequest source) {
-        mSettings = new CameraMetadataNative(source.mSettings);
-        setNativeInstance(mSettings);
+        mLogicalCameraId = new String(source.mLogicalCameraId);
+        for (Map.Entry<String, CameraMetadataNative> entry :
+                source.mPhysicalCameraSettings.entrySet()) {
+            mPhysicalCameraSettings.put(new String(entry.getKey()),
+                    new CameraMetadataNative(entry.getValue()));
+        }
+        mLogicalCameraSettings = mPhysicalCameraSettings.get(mLogicalCameraId);
+        setNativeInstance(mLogicalCameraSettings);
         mSurfaceSet.addAll(source.mSurfaceSet);
         mIsReprocess = source.mIsReprocess;
         mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -272,16 +282,35 @@
      *                               reprocess capture request to the same session where
      *                               the {@link TotalCaptureResult}, used to create the reprocess
      *                               capture, came from.
+     * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+     *                        Builder.
+     *
+     * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+     *                            the request for a specific physical camera.
      *
      * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
-     *                                  reprocessableSessionId.
+     *                                  reprocessableSessionId, or multiple physical cameras.
      *
      * @see CameraDevice#createReprocessCaptureRequest
      */
     private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
-            int reprocessableSessionId) {
-        mSettings = CameraMetadataNative.move(settings);
-        setNativeInstance(mSettings);
+            int reprocessableSessionId, String logicalCameraId, Set<String> physicalCameraIdSet) {
+        if ((physicalCameraIdSet != null) && isReprocess) {
+            throw new IllegalArgumentException("Create a reprocess capture request with " +
+                    "with more than one physical camera is not supported!");
+        }
+
+        mLogicalCameraId = logicalCameraId;
+        mLogicalCameraSettings = CameraMetadataNative.move(settings);
+        mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+        if (physicalCameraIdSet != null) {
+            for (String physicalId : physicalCameraIdSet) {
+                mPhysicalCameraSettings.put(physicalId, new CameraMetadataNative(
+                            mLogicalCameraSettings));
+            }
+        }
+
+        setNativeInstance(mLogicalCameraSettings);
         mIsReprocess = isReprocess;
         if (isReprocess) {
             if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) {
@@ -309,7 +338,7 @@
      */
     @Nullable
     public <T> T get(Key<T> key) {
-        return mSettings.get(key);
+        return mLogicalCameraSettings.get(key);
     }
 
     /**
@@ -319,7 +348,7 @@
     @SuppressWarnings("unchecked")
     @Override
     protected <T> T getProtected(Key<?> key) {
-        return (T) mSettings.get(key);
+        return (T) mLogicalCameraSettings.get(key);
     }
 
     /**
@@ -403,7 +432,7 @@
      * @hide
      */
     public CameraMetadataNative getNativeCopy() {
-        return new CameraMetadataNative(mSettings);
+        return new CameraMetadataNative(mLogicalCameraSettings);
     }
 
     /**
@@ -444,14 +473,16 @@
         return other != null
                 && Objects.equals(mUserTag, other.mUserTag)
                 && mSurfaceSet.equals(other.mSurfaceSet)
-                && mSettings.equals(other.mSettings)
+                && mPhysicalCameraSettings.equals(other.mPhysicalCameraSettings)
+                && mLogicalCameraId.equals(other.mLogicalCameraId)
+                && mLogicalCameraSettings.equals(other.mLogicalCameraSettings)
                 && mIsReprocess == other.mIsReprocess
                 && mReprocessableSessionId == other.mReprocessableSessionId;
     }
 
     @Override
     public int hashCode() {
-        return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag);
+        return HashCodeHelpers.hashCodeGeneric(mPhysicalCameraSettings, mSurfaceSet, mUserTag);
     }
 
     public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -479,8 +510,25 @@
      * @hide
      */
     private void readFromParcel(Parcel in) {
-        mSettings.readFromParcel(in);
-        setNativeInstance(mSettings);
+        int physicalCameraCount = in.readInt();
+        if (physicalCameraCount <= 0) {
+            throw new RuntimeException("Physical camera count" + physicalCameraCount +
+                    " should always be positive");
+        }
+
+        //Always start with the logical camera id
+        mLogicalCameraId = in.readString();
+        mLogicalCameraSettings = new CameraMetadataNative();
+        mLogicalCameraSettings.readFromParcel(in);
+        setNativeInstance(mLogicalCameraSettings);
+        mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+        for (int i = 1; i < physicalCameraCount; i++) {
+            String physicalId = in.readString();
+            CameraMetadataNative physicalCameraSettings = new CameraMetadataNative();
+            physicalCameraSettings.readFromParcel(in);
+            mPhysicalCameraSettings.put(physicalId, physicalCameraSettings);
+        }
+
         mIsReprocess = (in.readInt() == 0) ? false : true;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
 
@@ -509,7 +557,19 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        mSettings.writeToParcel(dest, flags);
+        int physicalCameraCount = mPhysicalCameraSettings.size();
+        dest.writeInt(physicalCameraCount);
+        //Logical camera id and settings always come first.
+        dest.writeString(mLogicalCameraId);
+        mLogicalCameraSettings.writeToParcel(dest, flags);
+        for (Map.Entry<String, CameraMetadataNative> entry : mPhysicalCameraSettings.entrySet()) {
+            if (entry.getKey().equals(mLogicalCameraId)) {
+                continue;
+            }
+            dest.writeString(entry.getKey());
+            entry.getValue().writeToParcel(dest, flags);
+        }
+
         dest.writeInt(mIsReprocess ? 1 : 0);
 
         synchronized (mSurfacesLock) {
@@ -542,6 +602,14 @@
     }
 
     /**
+     * Retrieves the logical camera id.
+     * @hide
+     */
+    public String getLogicalCameraId() {
+        return mLogicalCameraId;
+    }
+
+    /**
      * @hide
      */
     public void convertSurfaceToStreamId(
@@ -633,14 +701,20 @@
          *                               submits a reprocess capture request to the same session
          *                               where the {@link TotalCaptureResult}, used to create the
          *                               reprocess capture, came from.
+         * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+         *                        Builder.
+         * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+         *                            the request for a specific physical camera.
          *
          * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
          *                                  reprocessableSessionId.
          * @hide
          */
         public Builder(CameraMetadataNative template, boolean reprocess,
-                int reprocessableSessionId) {
-            mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId);
+                int reprocessableSessionId, String logicalCameraId,
+                Set<String> physicalCameraIdSet) {
+            mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId,
+                    logicalCameraId, physicalCameraIdSet);
         }
 
         /**
@@ -682,7 +756,7 @@
          * type to the key.
          */
         public <T> void set(@NonNull Key<T> key, T value) {
-            mRequest.mSettings.set(key, value);
+            mRequest.mLogicalCameraSettings.set(key, value);
         }
 
         /**
@@ -696,7 +770,71 @@
          */
         @Nullable
         public <T> T get(Key<T> key) {
-            return mRequest.mSettings.get(key);
+            return mRequest.mLogicalCameraSettings.get(key);
+        }
+
+        /**
+         * Set a capture request field to a value. The field definitions can be
+         * found in {@link CaptureRequest}.
+         *
+         * <p>Setting a field to {@code null} will remove that field from the capture request.
+         * Unless the field is optional, removing it will likely produce an error from the camera
+         * device when the request is submitted.</p>
+         *
+         *<p>This method can be called for logical camera devices, which are devices that have
+         * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+         * physical devices that are backing the logical camera. The camera Id included in the
+         * 'physicalCameraId' argument selects an individual physical device that will receive
+         * the customized capture request field.</p>
+         *
+         * @throws IllegalArgumentException if the physical camera id is not valid
+         *
+         * @param key The metadata field to write.
+         * @param value The value to set the field to, which must be of a matching
+         * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+         *                         via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+         * @return The builder object.
+         * type to the key.
+         */
+        public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value,
+                @NonNull String physicalCameraId) {
+            if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+                throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+                        " is not valid!");
+            }
+
+            mRequest.mPhysicalCameraSettings.get(physicalCameraId).set(key, value);
+
+            return this;
+        }
+
+        /**
+         * Get a capture request field value for a specific physical camera Id. The field
+         * definitions can be found in {@link CaptureRequest}.
+         *
+         *<p>This method can be called for logical camera devices, which are devices that have
+         * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+         * physical devices that are backing the logical camera. The camera Id included in the
+         * 'physicalCameraId' argument selects an individual physical device and returns
+         * its specific capture request field.</p>
+         *
+         * @throws IllegalArgumentException if the key or physical camera id were not valid
+         *
+         * @param key The metadata field to read.
+         * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+         *                         via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+         * @return The value of that key, or {@code null} if the field is not set.
+         */
+        @Nullable
+        public <T> T getPhysicalCameraKey(Key<T> key,@NonNull String physicalCameraId) {
+            if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+                throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+                        " is not valid!");
+            }
+
+            return mRequest.mPhysicalCameraSettings.get(physicalCameraId).get(key);
         }
 
         /**
@@ -748,7 +886,7 @@
          * @hide
          */
         public boolean isEmpty() {
-            return mRequest.mSettings.isEmpty();
+            return mRequest.mLogicalCameraSettings.isEmpty();
         }
 
     }
@@ -2619,6 +2757,29 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     * <p>When set to ON,
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * android.Statistics.info.availableOisDataModes</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see #STATISTICS_OIS_DATA_MODE_OFF
+     * @see #STATISTICS_OIS_DATA_MODE_ON
+     */
+    @PublicKey
+    public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+            new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+    /**
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 237a92d..d730fa8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2203,8 +2203,6 @@
      * significant illumination change, this value will be set to DETECTED for a single capture
      * result. Otherwise the value will be NOT_DETECTED. The threshold for detection is similar
      * to what would trigger a new passive focus scan to begin in CONTINUOUS autofocus modes.</p>
-     * <p>afSceneChange may be DETECTED only if afMode is AF_MODE_CONTINUOUS_VIDEO or
-     * AF_MODE_CONTINUOUS_PICTURE. In other AF modes, afSceneChange must be NOT_DETECTED.</p>
      * <p>This key will be available if the camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -3911,6 +3909,76 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     * <p>When set to ON,
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * android.Statistics.info.availableOisDataModes</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see #STATISTICS_OIS_DATA_MODE_OFF
+     * @see #STATISTICS_OIS_DATA_MODE_ON
+     */
+    @PublicKey
+    public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+            new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+    /**
+     * <p>An array of timestamps of OIS samples, in nanoseconds.</p>
+     * <p>The array contains the timestamps of OIS samples. The timestamps are in the same
+     * timebase as and comparable to {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
+     * <p><b>Units</b>: nanoseconds</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#SENSOR_TIMESTAMP
+     */
+    @PublicKey
+    public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
+            new Key<long[]>("android.statistics.oisTimestamps", long[].class);
+
+    /**
+     * <p>An array of shifts of OIS samples, in x direction.</p>
+     * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
+     * A positive value is a shift from left to right in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (3, 0) puts the new optical center at (1003, 500).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * <p><b>Units</b>: Pixels in active array.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     */
+    @PublicKey
+    public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
+            new Key<float[]>("android.statistics.oisXShifts", float[].class);
+
+    /**
+     * <p>An array of shifts of OIS samples, in y direction.</p>
+     * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
+     * A positive value is a shift from top to bottom in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (0, 5) puts the new optical center at (1000, 505).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * <p><b>Units</b>: Pixels in active array.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     */
+    @PublicKey
+    public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
+            new Key<float[]>("android.statistics.oisYShifts", float[].class);
+
+    /**
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 8c4dbfa..06c2c25 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -94,8 +94,8 @@
         // Note that after this step, the requestMetadata is mutated (swapped) and can not be used
         // for next request builder creation.
         CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
-                requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
-
+                requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                request.getLogicalCameraId(), /*physicalCameraIdSet*/ null);
         // Carry over userTag, as native metadata doesn't have this field.
         singleTargetRequestBuilder.setTag(request.getTag());
 
@@ -120,7 +120,8 @@
             // CaptureRequest.Builder creation.
             requestMetadata = new CameraMetadataNative(request.getNativeCopy());
             doubleTargetRequestBuilder = new CaptureRequest.Builder(
-                    requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+                    requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    request.getLogicalCameraId(), /*physicalCameraIdSet*/null);
             doubleTargetRequestBuilder.setTag(request.getTag());
             doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
                     CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4455d45..cab9d70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,13 +18,14 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
+import android.hardware.ICameraService;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.TotalCaptureResult;
@@ -34,7 +35,6 @@
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.hardware.camera2.utils.SurfaceUtils;
-import android.hardware.ICameraService;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -49,16 +49,15 @@
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -713,6 +712,38 @@
     }
 
     @Override
+    public CaptureRequest.Builder createCaptureRequest(int templateType,
+            Set<String> physicalCameraIdSet)
+            throws CameraAccessException {
+        synchronized(mInterfaceLock) {
+            checkIfCameraClosedOrInError();
+
+            for (String physicalId : physicalCameraIdSet) {
+                if (physicalId == getId()) {
+                    throw new IllegalStateException("Physical id matches the logical id!");
+                }
+            }
+
+            CameraMetadataNative templatedRequest = null;
+
+            templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
+
+            // If app target SDK is older than O, or it's not a still capture template, enableZsl
+            // must be false in the default request.
+            if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
+                    templateType != TEMPLATE_STILL_CAPTURE) {
+                overrideEnableZsl(templatedRequest, false);
+            }
+
+            CaptureRequest.Builder builder = new CaptureRequest.Builder(
+                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    getId(), physicalCameraIdSet);
+
+            return builder;
+        }
+    }
+
+    @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
         synchronized(mInterfaceLock) {
@@ -730,7 +761,8 @@
             }
 
             CaptureRequest.Builder builder = new CaptureRequest.Builder(
-                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    getId(), /*physicalCameraIdSet*/ null);
 
             return builder;
         }
@@ -746,7 +778,7 @@
                     CameraMetadataNative(inputResult.getNativeCopy());
 
             return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
-                    inputResult.getSessionId());
+                    inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
         }
     }
 
@@ -956,7 +988,8 @@
         // callback is valid
         handler = checkHandler(handler, callback);
 
-        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+        // 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
         for (CaptureRequest request : requestList) {
             if (request.getTargets().isEmpty()) {
                 throw new IllegalArgumentException(
@@ -967,7 +1000,20 @@
                 if (surface == null) {
                     throw new IllegalArgumentException("Null Surface targets are not allowed");
                 }
+
+                if (!request.isReprocess()) {
+                    continue;
+                }
+                for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                    OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
+                    if (configuration.isForPhysicalCamera()
+                            && configuration.getSurfaces().contains(surface)) {
+                        throw new IllegalArgumentException(
+                                "Reprocess request on physical stream is not allowed");
+                    }
+                }
             }
+
         }
 
         synchronized(mInterfaceLock) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a85b5f7..f47cd66 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -31,13 +31,12 @@
 import android.util.Size;
 import android.view.Surface;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-
 import static com.android.internal.util.Preconditions.*;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * A class for describing camera output, which contains a {@link Surface} and its specific
  * configuration for creating capture session.
@@ -266,6 +265,7 @@
         mConfiguredGenerationId = surface.getGenerationId();
         mIsDeferredConfig = false;
         mIsShared = false;
+        mPhysicalCameraId = null;
     }
 
     /**
@@ -319,6 +319,7 @@
         mConfiguredGenerationId = 0;
         mIsDeferredConfig = true;
         mIsShared = false;
+        mPhysicalCameraId = null;
     }
 
     /**
@@ -348,8 +349,9 @@
      * </ol>
      *
      * <p>To enable surface sharing, this function must be called before {@link
-     * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
-     * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
+     * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after
+     * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
      *
      * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
      * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
@@ -360,6 +362,44 @@
     }
 
     /**
+     * Set the id of the physical camera for this OutputConfiguration
+     *
+     * <p>In the case one logical camera is made up of multiple physical cameras, it could be
+     * desirable for the camera application to request streams from individual physical cameras.
+     * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
+     *
+     * <p>The valid physical camera id can be queried by {@link
+     * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
+     * </p>
+     *
+     * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
+     * stream.</p>
+     *
+     * <p>This function must be called before {@link
+     * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function
+     * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
+     *
+     * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
+     * or output of a reprocessing request. </p>
+     */
+    public void setPhysicalCameraId(@Nullable String physicalCameraId) {
+        mPhysicalCameraId = physicalCameraId;
+    }
+
+    /**
+     * Check if this configuration is for a physical camera.
+     *
+     * <p>This returns true if the output configuration was for a physical camera making up a
+     * logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p>
+     * @hide
+     */
+    public boolean isForPhysicalCamera() {
+        return (mPhysicalCameraId != null);
+    }
+
+    /**
      * Check if this configuration has deferred configuration.
      *
      * <p>This will return true if the output configuration was constructed with surface deferred by
@@ -487,6 +527,7 @@
         this.mConfiguredGenerationId = other.mConfiguredGenerationId;
         this.mIsDeferredConfig = other.mIsDeferredConfig;
         this.mIsShared = other.mIsShared;
+        this.mPhysicalCameraId = other.mPhysicalCameraId;
     }
 
     /**
@@ -502,6 +543,7 @@
         boolean isShared = source.readInt() == 1;
         ArrayList<Surface> surfaces = new ArrayList<Surface>();
         source.readTypedList(surfaces, Surface.CREATOR);
+        String physicalCameraId = source.readString();
 
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
 
@@ -524,6 +566,7 @@
                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
             mConfiguredGenerationId = 0;
         }
+        mPhysicalCameraId = physicalCameraId;
     }
 
     /**
@@ -622,6 +665,7 @@
         dest.writeInt(mIsDeferredConfig ? 1 : 0);
         dest.writeInt(mIsShared ? 1 : 0);
         dest.writeTypedList(mSurfaces);
+        dest.writeString(mPhysicalCameraId);
     }
 
     /**
@@ -675,13 +719,15 @@
         if (mIsDeferredConfig) {
             return HashCodeHelpers.hashCode(
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
-                    mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0);
+                    mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
+                    mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
         }
 
         return HashCodeHelpers.hashCode(
                 mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
                 mConfiguredSize.hashCode(), mConfiguredFormat,
-                mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0);
+                mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
+                mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -701,4 +747,6 @@
     private final boolean mIsDeferredConfig;
     // Flag indicating if this config has shared surfaces
     private boolean mIsShared;
+    // The physical camera id that this output configuration is for.
+    private String mPhysicalCameraId;
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 11d338d..166342d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3763,4 +3763,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * The network watchlist is a list of domains and IP addresses that are associated with
+     * potentially harmful apps. This method returns the hash of the watchlist currently
+     * used by the system.
+     *
+     * @return Hash of network watchlist config file. Null if config does not exist.
+     */
+    public byte[] getNetworkWatchlistConfigHash() {
+        try {
+            return mService.getNetworkWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a6fe738..ce95b60 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -180,4 +180,6 @@
     void stopKeepalive(in Network network, int slot);
 
     String getCaptivePortalServerUrl();
+
+    byte[] getNetworkWatchlistConfigHash();
 }
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 790c80b..eeb30e2 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -39,9 +39,9 @@
 
     void closeUdpEncapsulationSocket(int resourceId);
 
-    IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+    IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
 
-    void deleteTransportModeTransform(int transformId);
+    void deleteTransform(int transformId);
 
     void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
 
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index be6026f..37e2c4f 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -124,8 +124,7 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result =
-                        svc.createTransportModeTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
@@ -170,7 +169,7 @@
              * still want to clear out the transform.
              */
             IIpSecService svc = getIpSecService();
-            svc.deleteTransportModeTransform(mResourceId);
+            svc.deleteTransform(mResourceId);
             stopKeepalive();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a85f80e..01b2b39 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -160,13 +160,6 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
-        // TODO: fix the the telephony code to pass DEFAULT_NETWORK_YES and remove this constructor.
-        public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
-                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
-            this(iface, uid, set, tag, metered, roaming, DEFAULT_NETWORK_YES, rxBytes, rxPackets,
-                    txBytes, txPackets, operations);
-        }
-
         public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
                  int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
                  long operations) {
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b307c5d..8efd39a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -24,6 +24,15 @@
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -191,16 +200,30 @@
 
     private final String mNetworkId;
 
+    // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+    private final int mMetered;
+    private final int mRoaming;
+    private final int mDefaultNetwork;
+
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
     }
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
+        this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
+                DEFAULT_NETWORK_ALL);
+    }
+
+    public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
+            String networkId, int metered, int roaming, int defaultNetwork) {
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         mMatchSubscriberIds = matchSubscriberIds;
         mNetworkId = networkId;
+        mMetered = metered;
+        mRoaming = roaming;
+        mDefaultNetwork = defaultNetwork;
 
         if (!isKnownMatchRule(matchRule)) {
             Log.e(TAG, "Unknown network template rule " + matchRule
@@ -213,6 +236,9 @@
         mSubscriberId = in.readString();
         mMatchSubscriberIds = in.createStringArray();
         mNetworkId = in.readString();
+        mMetered = in.readInt();
+        mRoaming = in.readInt();
+        mDefaultNetwork = in.readInt();
     }
 
     @Override
@@ -221,6 +247,9 @@
         dest.writeString(mSubscriberId);
         dest.writeStringArray(mMatchSubscriberIds);
         dest.writeString(mNetworkId);
+        dest.writeInt(mMetered);
+        dest.writeInt(mRoaming);
+        dest.writeInt(mDefaultNetwork);
     }
 
     @Override
@@ -243,12 +272,23 @@
         if (mNetworkId != null) {
             builder.append(", networkId=").append(mNetworkId);
         }
+        if (mMetered != METERED_ALL) {
+            builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
+        }
+        if (mRoaming != ROAMING_ALL) {
+            builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
+        }
+        if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
+            builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
+                    mDefaultNetwork));
+        }
         return builder.toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
+        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
+                mDefaultNetwork);
     }
 
     @Override
@@ -257,7 +297,10 @@
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
                     && Objects.equals(mSubscriberId, other.mSubscriberId)
-                    && Objects.equals(mNetworkId, other.mNetworkId);
+                    && Objects.equals(mNetworkId, other.mNetworkId)
+                    && mMetered == other.mMetered
+                    && mRoaming == other.mRoaming
+                    && mDefaultNetwork == other.mDefaultNetwork;
         }
         return false;
     }
@@ -300,6 +343,10 @@
      * Test if given {@link NetworkIdentity} matches this template.
      */
     public boolean matches(NetworkIdentity ident) {
+        if (!matchesMetered(ident)) return false;
+        if (!matchesRoaming(ident)) return false;
+        if (!matchesDefaultNetwork(ident)) return false;
+
         switch (mMatchRule) {
             case MATCH_MOBILE_ALL:
                 return matchesMobile(ident);
@@ -326,6 +373,24 @@
         }
     }
 
+    private boolean matchesMetered(NetworkIdentity ident) {
+        return (mMetered == METERED_ALL)
+            || (mMetered == METERED_YES && ident.mMetered)
+            || (mMetered == METERED_NO && !ident.mMetered);
+    }
+
+    private boolean matchesRoaming(NetworkIdentity ident) {
+        return (mRoaming == ROAMING_ALL)
+            || (mRoaming == ROAMING_YES && ident.mRoaming)
+            || (mRoaming == ROAMING_NO && !ident.mRoaming);
+    }
+
+    private boolean matchesDefaultNetwork(NetworkIdentity ident) {
+        return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
+            || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
+            || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
+    }
+
     public boolean matchesSubscriberId(String subscriberId) {
         return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
     }
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 5425bf5..49047d3 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -86,4 +86,16 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get Network Watchlist config file hash.
+     */
+    public byte[] getWatchlistConfigHash() {
+        try {
+            return mNetworkWatchlistManager.getWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 49879a8..03a8dba 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -336,6 +336,9 @@
     private final StringBuilder mFormatBuilder = new StringBuilder(32);
     private final Formatter mFormatter = new Formatter(mFormatBuilder);
 
+    private static final String CELLULAR_CONTROLLER_NAME = "Cellular";
+    private static final String WIFI_CONTROLLER_NAME = "WiFi";
+
     /**
      * Indicates times spent by the uid at each cpu frequency in all process states.
      *
@@ -413,6 +416,13 @@
 
         /**
          * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * scan state.
+         */
+        public abstract LongCounter getScanTimeCounter();
+
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
          * receive state.
          */
         public abstract LongCounter getRxTimeCounter();
@@ -2399,6 +2409,14 @@
     public abstract long getWifiOnTime(long elapsedRealtimeUs, int which);
 
     /**
+     * Returns the time in microseconds that wifi has been active while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiActiveTime(long elapsedRealtimeUs, int which);
+
+    /**
      * Returns the time in microseconds that wifi has been on and the driver has
      * been in the running state while the device was running on battery.
      *
@@ -3345,6 +3363,20 @@
         final long sleepTimeMs
             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
 
+        if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
+            final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Scan time:  ");
+            formatTimeMs(sb, scanTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
         sb.setLength(0);
         sb.append(prefix);
         sb.append("     ");
@@ -3386,7 +3418,7 @@
 
         String [] powerLevel;
         switch(controllerName) {
-            case "Cellular":
+            case CELLULAR_CONTROLLER_NAME:
                 powerLevel = new String[] {
                     "   less than 0dBm: ",
                     "   0dBm to 8dBm: ",
@@ -4674,7 +4706,7 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        printControllerActivity(pw, sb, prefix, "Cellular",
+        printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
             getModemControllerActivity(), which);
 
         pw.print(prefix);
@@ -4683,6 +4715,16 @@
         sb.append("  Wifi Statistics:");
         pw.println(sb.toString());
 
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi kernel active time: ");
+        final long wifiActiveTime = getWifiActiveTime(rawRealtime, which);
+        formatTimeMs(sb, wifiActiveTime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(wifiActiveTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
         pw.print("     Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes));
         pw.print("     Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes));
         pw.print("     Wifi packets received: "); pw.println(wifiRxTotalPackets);
@@ -4760,7 +4802,8 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which);
+        printControllerActivity(pw, sb, prefix, WIFI_CONTROLLER_NAME,
+            getWifiControllerActivity(), which);
 
         pw.print(prefix);
         sb.setLength(0);
@@ -5238,8 +5281,8 @@
                 pw.println(sb.toString());
             }
 
-            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "Modem",
-                    u.getModemControllerActivity(), which);
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ",
+                CELLULAR_CONTROLLER_NAME, u.getModemControllerActivity(), which);
 
             if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) {
                 pw.print(prefix); pw.print("    Wi-Fi network: ");
@@ -5293,7 +5336,7 @@
                 pw.println(sb.toString());
             }
 
-            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "WiFi",
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ", WIFI_CONTROLLER_NAME,
                     u.getWifiControllerActivity(), which);
 
             if (btRxBytes > 0 || btTxBytes > 0) {
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 94a44ec..dda0ed8 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -82,6 +82,14 @@
     public static final String ACTION_UPDATE_SMART_SELECTION
             = "android.intent.action.UPDATE_SMART_SELECTION";
 
+    /**
+     * Update network watchlist config file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_NETWORK_WATCHLIST
+            = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
+
     private ConfigUpdate() {
     }
 }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 848ab88..33e8c3e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2352,22 +2352,28 @@
     }
 
     /**
-     * Attach a library as a jvmti agent to the current runtime.
+     * Attach a library as a jvmti agent to the current runtime, with the given classloader
+     * determining the library search path.
+     * <p>
+     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+     * throw a SecurityException.
      *
-     * @param library library containing the agent
-     * @param options options passed to the agent
+     * @param library the library containing the agent.
+     * @param options the options passed to the agent.
+     * @param classLoader the classloader determining the library search path.
      *
-     * @throws IOException If the agent could not be attached
+     * @throws IOException if the agent could not be attached.
+     * @throws SecurityException if the app is not debuggable.
      */
-    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
-            throws IOException {
+    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+            @Nullable ClassLoader classLoader) throws IOException {
         Preconditions.checkNotNull(library);
         Preconditions.checkArgument(!library.contains("="));
 
         if (options == null) {
-            VMDebug.attachAgent(library);
+            VMDebug.attachAgent(library, classLoader);
         } else {
-            VMDebug.attachAgent(library + "=" + options);
+            VMDebug.attachAgent(library + "=" + options, classLoader);
         }
     }
 }
diff --git a/core/java/android/os/ISystemUpdateManager.aidl b/core/java/android/os/ISystemUpdateManager.aidl
new file mode 100644
index 0000000..f7f5079
--- /dev/null
+++ b/core/java/android/os/ISystemUpdateManager.aidl
@@ -0,0 +1,27 @@
+/* //device/java/android/android/os/ISystemUpdateInfo.aidl
+**
+** 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.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISystemUpdateManager {
+    Bundle retrieveSystemUpdateInfo();
+    void updateSystemUpdateInfo(in PersistableBundle data);
+}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 57db9d1..3e8e885 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -755,7 +755,9 @@
         // Block until the ordered broadcast has completed.
         condition.block();
 
-        wipeEuiccData(context, wipeEuicc, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
+        if (wipeEuicc) {
+            wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
+        }
 
         String shutdownArg = null;
         if (shutdown) {
@@ -774,13 +776,11 @@
     /**
      * Returns whether wipe Euicc data successfully or not.
      *
-     * @param isWipeEuicc whether we want to wipe Euicc data or not
      * @param packageName the package name of the caller app.
      *
      * @hide
      */
-    public static boolean wipeEuiccData(
-            Context context, final boolean isWipeEuicc, final String packageName) {
+    public static boolean wipeEuiccData(Context context, final String packageName) {
         ContentResolver cr = context.getContentResolver();
         if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
             // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles,
@@ -802,19 +802,10 @@
                         if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
                             int detailedCode = intent.getIntExtra(
                                     EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
-                            if (isWipeEuicc) {
-                                Log.e(TAG, "Error wiping euicc data, Detailed code = "
-                                        + detailedCode);
-                            } else {
-                                Log.e(TAG, "Error retaining euicc data, Detailed code = "
-                                        + detailedCode);
-                            }
+                            Log.e(TAG, "Error wiping euicc data, Detailed code = "
+                                    + detailedCode);
                         } else {
-                            if (isWipeEuicc) {
-                                Log.d(TAG, "Successfully wiped euicc data.");
-                            } else {
-                                Log.d(TAG, "Successfully retained euicc data.");
-                            }
+                            Log.d(TAG, "Successfully wiped euicc data.");
                             wipingSucceeded.set(true /* newValue */);
                         }
                         euiccFactoryResetLatch.countDown();
@@ -833,11 +824,7 @@
             Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
             context.getApplicationContext()
                     .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler);
-            if (isWipeEuicc) {
-                euiccManager.eraseSubscriptions(callbackIntent);
-            } else {
-                euiccManager.retainSubscriptionsForFactoryReset(callbackIntent);
-            }
+            euiccManager.eraseSubscriptions(callbackIntent);
             try {
                 long waitingTimeMillis = Settings.Global.getLong(
                         context.getContentResolver(),
@@ -849,20 +836,12 @@
                     waitingTimeMillis = MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS;
                 }
                 if (!euiccFactoryResetLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
-                    if (isWipeEuicc) {
-                        Log.e(TAG, "Timeout wiping eUICC data.");
-                    } else {
-                        Log.e(TAG, "Timeout retaining eUICC data.");
-                    }
+                    Log.e(TAG, "Timeout wiping eUICC data.");
                     return false;
                 }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
-                if (isWipeEuicc) {
-                    Log.e(TAG, "Wiping eUICC data interrupted", e);
-                } else {
-                    Log.e(TAG, "Retaining eUICC data interrupted", e);
-                }
+                Log.e(TAG, "Wiping eUICC data interrupted", e);
                 return false;
             } finally {
                 context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 4f6d322..a9b8675 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.util.Log;
 import android.util.MutableInt;
 
@@ -33,6 +34,7 @@
  *
  * {@hide}
  */
+@SystemApi
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -40,9 +42,11 @@
     /**
      * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
      * uses reflection to read this whenever text is selected (http://b/36095274).
+     * @hide
      */
     public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
 
+    /** @hide */
     public static final int PROP_VALUE_MAX = 91;
 
     @GuardedBy("sChangeCallbacks")
@@ -86,8 +90,10 @@
      *
      * @param key the key to lookup
      * @return an empty string if the {@code key} isn't found
+     * @hide
      */
     @NonNull
+    @SystemApi
     public static String get(@NonNull String key) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
@@ -100,8 +106,10 @@
      * @param def the default value in case the property is not set or empty
      * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
      * string otherwise
+     * @hide
      */
     @NonNull
+    @SystemApi
     public static String get(@NonNull String key, @Nullable String def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
@@ -114,7 +122,9 @@
      * @param def a default value to return
      * @return the key parsed as an integer, or def if the key isn't found or
      *         cannot be parsed
+     * @hide
      */
+    @SystemApi
     public static int getInt(@NonNull String key, int def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
@@ -127,7 +137,9 @@
      * @param def a default value to return
      * @return the key parsed as a long, or def if the key isn't found or
      *         cannot be parsed
+     * @hide
      */
+    @SystemApi
     public static long getLong(@NonNull String key, long def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
@@ -145,7 +157,9 @@
      * @param def a default value to return
      * @return the key parsed as a boolean, or def if the key isn't found or is
      *         not able to be parsed as a boolean.
+     * @hide
      */
+    @SystemApi
     public static boolean getBoolean(@NonNull String key, boolean def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
@@ -155,6 +169,7 @@
      * Set the value for the given {@code key} to {@code val}.
      *
      * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+     * @hide
      */
     public static void set(@NonNull String key, @Nullable String val) {
         if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
@@ -170,6 +185,7 @@
      *
      * @param callback The {@link Runnable} that should be executed when a system property
      * changes.
+     * @hide
      */
     public static void addChangeCallback(@NonNull Runnable callback) {
         synchronized (sChangeCallbacks) {
@@ -194,10 +210,14 @@
         }
     }
 
-    /*
+    /**
      * Notifies listeners that a system property has changed
+     * @hide
      */
     public static void reportSyspropChanged() {
         native_report_sysprop_change();
     }
+
+    private SystemProperties() {
+    }
 }
diff --git a/core/java/android/os/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
new file mode 100644
index 0000000..ce3e2259
--- /dev/null
+++ b/core/java/android/os/SystemUpdateManager.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Allows querying and posting system update information.
+ *
+ * {@hide}
+ */
+@SystemApi
+@SystemService(Context.SYSTEM_UPDATE_SERVICE)
+public class SystemUpdateManager {
+    private static final String TAG = "SystemUpdateManager";
+
+    /** The status key of the system update info, expecting an int value. */
+    @SystemApi
+    public static final String KEY_STATUS = "status";
+
+    /** The title of the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TITLE = "title";
+
+    /** Whether it is a security update, expecting a boolean value. */
+    @SystemApi
+    public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
+
+    /** The build fingerprint after installing the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+
+    /** The security patch level after installing the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+
+    /**
+     * The KEY_STATUS value that indicates there's no update status info available.
+     */
+    @SystemApi
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * The KEY_STATUS value that indicates there's no pending update.
+     */
+    @SystemApi
+    public static final int STATUS_IDLE = 1;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for download, but pending user
+     * approval to start.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_DOWNLOAD = 2;
+
+    /**
+     * The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
+     * has started).
+     */
+    @SystemApi
+    public static final int STATUS_IN_PROGRESS = 3;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for install.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_INSTALL = 4;
+
+    /**
+     * The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
+     * to both of A/B and non-A/B OTAs.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_REBOOT = 5;
+
+    private final ISystemUpdateManager mService;
+
+    /** @hide */
+    public SystemUpdateManager(ISystemUpdateManager service) {
+        mService = checkNotNull(service, "missing ISystemUpdateManager");
+    }
+
+    /**
+     * Queries the current pending system update info.
+     *
+     * <p>Requires the {@link android.Manifest.permission#READ_SYSTEM_UPDATE_INFO} or
+     * {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @return A {@code Bundle} that contains the pending system update information in key-value
+     * pairs.
+     *
+     * @throws SecurityException if the caller is not allowed to read the info.
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
+            android.Manifest.permission.RECOVERY,
+    })
+    public Bundle retrieveSystemUpdateInfo() {
+        try {
+            return mService.retrieveSystemUpdateInfo();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allows a system updater to publish the pending update info.
+     *
+     * <p>The reported info will not persist across reboots. Because only the reporting updater
+     * understands the criteria to determine a successful/failed update.
+     *
+     * <p>Requires the {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @param infoBundle The {@code PersistableBundle} that contains the system update information,
+     * such as the current update status. {@link #KEY_STATUS} is required in the bundle.
+     *
+     * @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
+     * @throws SecurityException if the caller is not allowed to update the info.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+        if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
+            throw new IllegalArgumentException("Missing status in the bundle");
+        }
+        try {
+            mService.updateSystemUpdateInfo(infoBundle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/core/java/android/os/connectivity/WifiBatteryStats.aidl
similarity index 66%
rename from packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
rename to core/java/android/os/connectivity/WifiBatteryStats.aidl
index dbc61c2..12ac738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.aidl
@@ -14,15 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.core.instrumentation;
+package android.os.connectivity;
 
-public interface Instrumentable {
-
-    int METRICS_CATEGORY_UNKNOWN = 0;
-
-    /**
-     * Instrumented name for a view as defined in
-     * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}.
-     */
-    int getMetricsCategory();
-}
+/** {@hide} */
+parcelable WifiBatteryStats;
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
new file mode 100644
index 0000000..e5341ee
--- /dev/null
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.connectivity;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * API for Wifi power stats
+ *
+ * @hide
+ */
+public final class WifiBatteryStats implements Parcelable {
+
+  private long mLoggingDurationMs;
+  private long mKernelActiveTimeMs;
+  private long mNumPacketsTx;
+  private long mNumBytesTx;
+  private long mNumPacketsRx;
+  private long mNumBytesRx;
+  private long mSleepTimeMs;
+  private long mScanTimeMs;
+  private long mIdleTimeMs;
+  private long mRxTimeMs;
+  private long mTxTimeMs;
+  private long mEnergyConsumedMaMs;
+  private long mNumAppScanRequest;
+  private long[] mTimeInStateMs;
+  private long[] mTimeInSupplicantStateMs;
+  private long[] mTimeInRxSignalStrengthLevelMs;
+
+  public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
+      Parcelable.Creator<WifiBatteryStats>() {
+        public WifiBatteryStats createFromParcel(Parcel in) {
+          return new WifiBatteryStats(in);
+        }
+
+        public WifiBatteryStats[] newArray(int size) {
+          return new WifiBatteryStats[size];
+        }
+      };
+
+  public WifiBatteryStats() {
+    initialize();
+  }
+
+  public void writeToParcel(Parcel out, int flags) {
+    out.writeLong(mLoggingDurationMs);
+    out.writeLong(mKernelActiveTimeMs);
+    out.writeLong(mNumPacketsTx);
+    out.writeLong(mNumBytesTx);
+    out.writeLong(mNumPacketsRx);
+    out.writeLong(mNumBytesRx);
+    out.writeLong(mSleepTimeMs);
+    out.writeLong(mScanTimeMs);
+    out.writeLong(mIdleTimeMs);
+    out.writeLong(mRxTimeMs);
+    out.writeLong(mTxTimeMs);
+    out.writeLong(mEnergyConsumedMaMs);
+    out.writeLong(mNumAppScanRequest);
+    out.writeLongArray(mTimeInStateMs);
+    out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
+    out.writeLongArray(mTimeInSupplicantStateMs);
+  }
+
+  public void readFromParcel(Parcel in) {
+    mLoggingDurationMs = in.readLong();
+    mKernelActiveTimeMs = in.readLong();
+    mNumPacketsTx = in.readLong();
+    mNumBytesTx = in.readLong();
+    mNumPacketsRx = in.readLong();
+    mNumBytesRx = in.readLong();
+    mSleepTimeMs = in.readLong();
+    mScanTimeMs = in.readLong();
+    mIdleTimeMs = in.readLong();
+    mRxTimeMs = in.readLong();
+    mTxTimeMs = in.readLong();
+    mEnergyConsumedMaMs = in.readLong();
+    mNumAppScanRequest = in.readLong();
+    in.readLongArray(mTimeInStateMs);
+    in.readLongArray(mTimeInRxSignalStrengthLevelMs);
+    in.readLongArray(mTimeInSupplicantStateMs);
+  }
+
+  public long getLoggingDurationMs() {
+    return mLoggingDurationMs;
+  }
+
+  public long getKernelActiveTimeMs() {
+    return mKernelActiveTimeMs;
+  }
+
+  public long getNumPacketsTx() {
+    return mNumPacketsTx;
+  }
+
+  public long getNumBytesTx() {
+    return mNumBytesTx;
+  }
+
+  public long getNumPacketsRx() {
+    return mNumPacketsRx;
+  }
+
+  public long getNumBytesRx() {
+    return mNumBytesRx;
+  }
+
+  public long getSleepTimeMs() {
+    return mSleepTimeMs;
+  }
+
+  public long getScanTimeMs() {
+    return mScanTimeMs;
+  }
+
+  public long getIdleTimeMs() {
+    return mIdleTimeMs;
+  }
+
+  public long getRxTimeMs() {
+    return mRxTimeMs;
+  }
+
+  public long getTxTimeMs() {
+    return mTxTimeMs;
+  }
+
+  public long getEnergyConsumedMaMs() {
+    return mEnergyConsumedMaMs;
+  }
+
+  public long getNumAppScanRequest() {
+    return mNumAppScanRequest;
+  }
+
+  public long[] getTimeInStateMs() {
+    return mTimeInStateMs;
+  }
+
+  public long[] getTimeInRxSignalStrengthLevelMs() {
+    return mTimeInRxSignalStrengthLevelMs;
+  }
+
+  public long[] getTimeInSupplicantStateMs() {
+    return mTimeInSupplicantStateMs;
+  }
+
+  public void setLoggingDurationMs(long t) {
+    mLoggingDurationMs = t;
+    return;
+  }
+
+  public void setKernelActiveTimeMs(long t) {
+    mKernelActiveTimeMs = t;
+    return;
+  }
+
+  public void setNumPacketsTx(long n) {
+    mNumPacketsTx = n;
+    return;
+  }
+
+  public void setNumBytesTx(long b) {
+    mNumBytesTx = b;
+    return;
+  }
+
+  public void setNumPacketsRx(long n) {
+    mNumPacketsRx = n;
+    return;
+  }
+
+  public void setNumBytesRx(long b) {
+    mNumBytesRx = b;
+    return;
+  }
+
+  public void setSleepTimeMs(long t) {
+    mSleepTimeMs = t;
+    return;
+  }
+
+  public void setScanTimeMs(long t) {
+    mScanTimeMs = t;
+    return;
+  }
+
+  public void setIdleTimeMs(long t) {
+    mIdleTimeMs = t;
+    return;
+  }
+
+  public void setRxTimeMs(long t) {
+    mRxTimeMs = t;
+    return;
+  }
+
+  public void setTxTimeMs(long t) {
+    mTxTimeMs = t;
+    return;
+  }
+
+  public void setEnergyConsumedMaMs(long e) {
+    mEnergyConsumedMaMs = e;
+    return;
+  }
+
+  public void setNumAppScanRequest(long n) {
+    mNumAppScanRequest = n;
+    return;
+  }
+
+  public void setTimeInStateMs(long[] t) {
+    mTimeInStateMs = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, BatteryStats.NUM_WIFI_STATES));
+    return;
+  }
+
+  public void setTimeInRxSignalStrengthLevelMs(long[] t) {
+    mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS));
+    return;
+  }
+
+  public void setTimeInSupplicantStateMs(long[] t) {
+    mTimeInSupplicantStateMs = Arrays.copyOfRange(
+        t, 0, Math.min(t.length, BatteryStats.NUM_WIFI_SUPPL_STATES));
+    return;
+  }
+
+  public int describeContents() {
+    return 0;
+  }
+
+  private WifiBatteryStats(Parcel in) {
+    initialize();
+    readFromParcel(in);
+  }
+
+  private void initialize() {
+    mLoggingDurationMs = 0;
+    mKernelActiveTimeMs = 0;
+    mNumPacketsTx = 0;
+    mNumBytesTx = 0;
+    mNumPacketsRx = 0;
+    mNumBytesRx = 0;
+    mSleepTimeMs = 0;
+    mScanTimeMs = 0;
+    mIdleTimeMs = 0;
+    mRxTimeMs = 0;
+    mTxTimeMs = 0;
+    mEnergyConsumedMaMs = 0;
+    mNumAppScanRequest = 0;
+    mTimeInStateMs = new long[BatteryStats.NUM_WIFI_STATES];
+    Arrays.fill(mTimeInStateMs, 0);
+    mTimeInRxSignalStrengthLevelMs = new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS];
+    Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
+    mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
+    Arrays.fill(mTimeInSupplicantStateMs, 0);
+    return;
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 2eca4c98..9ac2b3e 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -33,7 +33,6 @@
 public class RapporEncoder implements DifferentialPrivacyEncoder {
 
     // Hard-coded seed and secret for insecure encoder
-    private static final long INSECURE_RANDOM_SEED = 0x12345678L;
     private static final byte[] INSECURE_SECRET = new byte[]{
             (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
             (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
@@ -66,8 +65,8 @@
             // Use SecureRandom as random generator.
             random = sSecureRandom;
         } else {
-            // Hard-coded random generator, to have deterministic result.
-            random = new Random(INSECURE_RANDOM_SEED);
+            // To have deterministic result by hard coding encoder id as seed.
+            random = new Random((long) config.mEncoderId.hashCode());
             userSecret = INSECURE_SECRET;
         }
         mEncoder = new Encoder(random, null, null,
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 2169457..7ad9e01 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -154,9 +154,12 @@
     public static final String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
 
     /**
-     * Activity Action: Dismiss timers.
+     * Activity Action: Dismiss a timer.
      * <p>
-     * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op.
+     * The timer to dismiss should be specified using the Intent's data URI, which represents a
+     * deeplink to the timer.
+     * </p><p>
+     * If no data URI is provided, dismiss all expired timers.
      * </p>
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 60df467..c6c8d9d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,13 @@
         /** Call was WIFI call. */
         public static final int FEATURES_WIFI = 1 << 3;
 
-        /** Call was on RTT at some point */
-        public static final int FEATURES_RTT = 1 << 4;
-
         /**
          * Indicates the call underwent Assisted Dialing.
-         * @hide
          */
-        public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+        public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
+
+        /** Call was on RTT at some point */
+        public static final int FEATURES_RTT = 1 << 5;
 
         /**
          * The phone number as the user entered it.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b2cc18b..0e323f8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9424,6 +9424,14 @@
        public static final String WIFI_VERBOSE_LOGGING_ENABLED =
                "wifi_verbose_logging_enabled";
 
+        /**
+         * Setting to enable connected MAC randomization in Wi-Fi; disabled by default, and
+         * setting to 1 will enable it. In the future, additional values may be supported.
+         * @hide
+         */
+        public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
+                "wifi_connected_mac_randomization_enabled";
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -10128,7 +10136,8 @@
          * This is encoded as a key=value list, separated by commas. Ex:
          *
          * "battery_tip_enabled=true,summary_enabled=true,high_usage_enabled=true,"
-         * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50"
+         * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50,"
+         * "high_usage_battery_draining=25,high_usage_period_ms=3000"
          *
          * The following keys are supported:
          *
@@ -10138,6 +10147,8 @@
          * battery_saver_tip_enabled        (boolean)
          * high_usage_enabled               (boolean)
          * high_usage_app_count             (int)
+         * high_usage_period_ms             (long)
+         * high_usage_battery_draining      (int)
          * app_restriction_enabled          (boolean)
          * reduced_battery_enabled          (boolean)
          * reduced_battery_percent          (int)
@@ -10348,6 +10359,8 @@
          * The following keys are supported:
          * <pre>
          * track_cpu_times_by_proc_state (boolean)
+         * track_cpu_active_cluster_time (boolean)
+         * read_binary_cpu_time          (boolean)
          * </pre>
          *
          * <p>
@@ -10375,6 +10388,15 @@
         public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
 
         /**
+         * Whether or not to enable Forced App Standby on small battery devices.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+                = "forced_app_standby_for_small_battery_enabled";
+
+        /**
          * Whether or not Network Watchlist feature is enabled.
          * Type: int (0 for false, 1 for true)
          * Default: 0
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f409e5b..72afbb8 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -73,6 +73,7 @@
     public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
+    public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 4e4caf0..df63a91 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -99,7 +99,9 @@
                 final String[] userDataValues = (String[]) args.arg5;
                 final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
                         Arrays.asList(userDataValues));
-                data.putParcelable(EXTRA_SCORES, new Scores(scores));
+                if (scores != null) {
+                    data.putParcelable(EXTRA_SCORES, new Scores(scores));
+                }
                 break;
             default:
                 Log.w(TAG, "Handling unknown message: " + action);
@@ -148,7 +150,8 @@
     public float[][] onGetScores(@Nullable String algorithm,
             @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
-        throw new UnsupportedOperationException("Must be implemented by external service");
+        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+        return null;
     }
 
     private final class AutofillFieldClassificationServiceWrapper
@@ -182,7 +185,7 @@
             }
         }
 
-        private  Scores(float[][] scores) {
+        private Scores(float[][] scores) {
             this.scores = scores;
         }
 
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml b/core/java/android/service/notification/NotifyingApp.aidl
similarity index 67%
copy from packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
copy to core/java/android/service/notification/NotifyingApp.aidl
index 5d5c425..5358c2f 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
+++ b/core/java/android/service/notification/NotifyingApp.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /**
- * Copyright (c) 2017, The Android Open Source Project
+ * Copyright (c) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,10 +13,7 @@
  * 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">Display Cutout Emulation</string>
+package android.service.notification;
 
-</resources>
-
+parcelable NotifyingApp;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java
new file mode 100644
index 0000000..38f18c6
--- /dev/null
+++ b/core/java/android/service/notification/NotifyingApp.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> {
+
+    private int mUid;
+    private String mPkg;
+    private long mLastNotified;
+
+    public NotifyingApp() {}
+
+    protected NotifyingApp(Parcel in) {
+        mUid = in.readInt();
+        mPkg = in.readString();
+        mLastNotified = in.readLong();
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Sets the uid of the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setUid(int mUid) {
+        this.mUid = mUid;
+        return this;
+    }
+
+    public String getPackage() {
+        return mPkg;
+    }
+
+    /**
+     * Sets the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setPackage(@NonNull String mPkg) {
+        this.mPkg = mPkg;
+        return this;
+    }
+
+    public long getLastNotified() {
+        return mLastNotified;
+    }
+
+    /**
+     * Sets the time the notification was originally sent. Returns self.
+     */
+    public NotifyingApp setLastNotified(long mLastNotified) {
+        this.mLastNotified = mLastNotified;
+        return this;
+    }
+
+    public static final Creator<NotifyingApp> CREATOR = new Creator<NotifyingApp>() {
+        @Override
+        public NotifyingApp createFromParcel(Parcel in) {
+            return new NotifyingApp(in);
+        }
+
+        @Override
+        public NotifyingApp[] newArray(int size) {
+            return new NotifyingApp[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUid);
+        dest.writeString(mPkg);
+        dest.writeLong(mLastNotified);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotifyingApp that = (NotifyingApp) o;
+        return getUid() == that.getUid()
+                && getLastNotified() == that.getLastNotified()
+                && Objects.equals(mPkg, that.mPkg);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUid(), mPkg, getLastNotified());
+    }
+
+    /**
+     * Sorts notifying apps from newest last notified date to oldest.
+     */
+    @Override
+    public int compareTo(NotifyingApp o) {
+        if (getLastNotified() == o.getLastNotified()) {
+            if (getUid() == o.getUid()) {
+                return getPackage().compareTo(o.getPackage());
+            }
+            return Integer.compare(getUid(), o.getUid());
+        }
+
+        return -Long.compare(getLastNotified(), o.getLastNotified());
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyingApp{"
+                + "mUid=" + mUid
+                + ", mPkg='" + mPkg + '\''
+                + ", mLastNotified=" + mLastNotified
+                + '}';
+    }
+}
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 908ef55..3b4eea7 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -16,71 +16,105 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that changes the size of the text it's attached to.
+ * <p>
+ * For example, the size of the text can be changed to 55dp like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with absolute size span");
+ *string.setSpan(new AbsoluteSizeSpan(55, true), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" />
+ * <figcaption>Text with text size updated.</figcaption>
+ */
 public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final int mSize;
-    private boolean mDip;
+    private final boolean mDip;
 
     /**
      * Set the text size to <code>size</code> physical pixels.
      */
     public AbsoluteSizeSpan(int size) {
-        mSize = size;
+        this(size, false);
     }
 
     /**
-     * Set the text size to <code>size</code> physical pixels,
-     * or to <code>size</code> device-independent pixels if
-     * <code>dip</code> is true.
+     * Set the text size to <code>size</code> physical pixels, or to <code>size</code>
+     * device-independent pixels if <code>dip</code> is true.
      */
     public AbsoluteSizeSpan(int size, boolean dip) {
         mSize = size;
         mDip = dip;
     }
 
-    public AbsoluteSizeSpan(Parcel src) {
+    /**
+     * Creates an {@link AbsoluteSizeSpan} from a parcel.
+     */
+    public AbsoluteSizeSpan(@NonNull Parcel src) {
         mSize = src.readInt();
         mDip = src.readInt() != 0;
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.ABSOLUTE_SIZE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mSize);
         dest.writeInt(mDip ? 1 : 0);
     }
 
+    /**
+     * Get the text size. This is in physical pixels if {@link #getDip()} returns false or in
+     * device-independent pixels if {@link #getDip()} returns true.
+     *
+     * @return the text size, either in physical pixels or device-independent pixels.
+     * @see AbsoluteSizeSpan#AbsoluteSizeSpan(int, boolean)
+     */
     public int getSize() {
         return mSize;
     }
 
+    /**
+     * Returns whether the size is in device-independent pixels or not, depending on the
+     * <code>dip</code> flag passed in {@link #AbsoluteSizeSpan(int, boolean)}
+     *
+     * @return <code>true</code> if the size is in device-independent pixels, <code>false</code>
+     * otherwise
+     *
+     * @see #AbsoluteSizeSpan(int, boolean)
+     */
     public boolean getDip() {
         return mDip;
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         if (mDip) {
             ds.setTextSize(mSize * ds.density);
         } else {
@@ -89,7 +123,7 @@
     }
 
     @Override
-    public void updateMeasureState(TextPaint ds) {
+    public void updateMeasureState(@NonNull TextPaint ds) {
         if (mDip) {
             ds.setTextSize(mSize * ds.density);
         } else {
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index 4f471a8..44e35615 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -27,11 +27,10 @@
  * Changes the background color of the text to which the span is attached.
  * <p>
  * For example, to set a green background color for a text you would create a {@link
- * android.text.SpannableStringBuilder} based on the text and set the span.
+ * android.text.SpannableString} based on the text and set the span.
  * <pre>{@code
  * SpannableString string = new SpannableString("Text with a background color span");
- *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- * }</pre>
+ *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
  * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
  * <figcaption>Set a background color for the text.</figcaption>
  */
@@ -58,30 +57,29 @@
         mColor = src.readInt();
     }
 
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.BACKGROUND_COLOR_SPAN;
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written.
-     */
+    @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
+    @Override
     public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 43dd0ff..70175c8 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -16,6 +16,11 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
@@ -26,38 +31,108 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 
+/**
+ * A span which styles paragraphs as bullet points (respecting layout direction).
+ * <p>
+ * BulletSpans must be attached from the first character to the last character of a single
+ * paragraph, otherwise the bullet point will not be displayed but the first paragraph encountered
+ * will have a leading margin.
+ * <p>
+ * BulletSpans allow configuring the following elements:
+ * <ul>
+ * <li><b>gap width</b> - the distance, in pixels, between the bullet point and the paragraph.
+ * Default value is 2px.</li>
+ * <li><b>color</b> - the bullet point color. By default, the bullet point color is 0 - no color,
+ * so it uses the TextView's text color.</li>
+ * <li><b>bullet radius</b> - the radius, in pixels, of the bullet point. Default value is
+ * 4px.</li>
+ * </ul>
+ * For example, a BulletSpan using the default values can be constructed like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/defaultbulletspan.png" />
+ * <figcaption>BulletSpan constructed with default values.</figcaption>
+ * <p>
+ * <p>
+ * To construct a BulletSpan with a gap width of 40px, green bullet point and bullet radius of
+ * 20px:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(40, color, 20), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/custombulletspan.png" />
+ * <figcaption>Customized BulletSpan.</figcaption>
+ */
 public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
-    private final int mGapWidth;
-    private final boolean mWantColor;
-    private final int mColor;
-
     // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
-    private static final float BULLET_RADIUS = 3 * 1.2f;
-    private static Path sBulletPath = null;
+    private static final int STANDARD_BULLET_RADIUS = 4;
     public static final int STANDARD_GAP_WIDTH = 2;
+    private static final int STANDARD_COLOR = 0;
 
+    @Px
+    private final int mGapWidth;
+    @Px
+    private final int mBulletRadius;
+    private Path mBulletPath = null;
+    @ColorInt
+    private final int mColor;
+    private final boolean mWantColor;
+
+    /**
+     * Creates a {@link BulletSpan} with the default values.
+     */
     public BulletSpan() {
-        mGapWidth = STANDARD_GAP_WIDTH;
-        mWantColor = false;
-        mColor = 0;
+        this(STANDARD_GAP_WIDTH, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
+    /**
+     * Creates a {@link BulletSpan} based on a gap width
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     */
     public BulletSpan(int gapWidth) {
-        mGapWidth = gapWidth;
-        mWantColor = false;
-        mColor = 0;
+        this(gapWidth, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
-    public BulletSpan(int gapWidth, int color) {
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     * @param color    the bullet point color, as a color integer
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color) {
+        this(gapWidth, color, true, STANDARD_BULLET_RADIUS);
+    }
+
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth     the distance, in pixels, between the bullet point and the paragraph.
+     * @param color        the bullet point color, as a color integer.
+     * @param bulletRadius the radius of the bullet point, in pixels.
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color, @IntRange(from = 0) int bulletRadius) {
+        this(gapWidth, color, true, bulletRadius);
+    }
+
+    private BulletSpan(int gapWidth, @ColorInt int color, boolean wantColor,
+            @IntRange(from = 0) int bulletRadius) {
         mGapWidth = gapWidth;
-        mWantColor = true;
+        mBulletRadius = bulletRadius;
         mColor = color;
+        mWantColor = wantColor;
     }
 
-    public BulletSpan(Parcel src) {
+    /**
+     * Creates a {@link BulletSpan} from a parcel.
+     */
+    public BulletSpan(@NonNull Parcel src) {
         mGapWidth = src.readInt();
         mWantColor = src.readInt() != 0;
         mColor = src.readInt();
+        mBulletRadius = src.readInt();
     }
 
     @Override
@@ -77,68 +152,97 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
     @Override
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mGapWidth);
         dest.writeInt(mWantColor ? 1 : 0);
         dest.writeInt(mColor);
+        dest.writeInt(mBulletRadius);
     }
 
     @Override
     public int getLeadingMargin(boolean first) {
-        return (int) (2 * BULLET_RADIUS + mGapWidth);
+        return 2 * mBulletRadius + mGapWidth;
+    }
+
+    /**
+     * Get the distance, in pixels, between the bullet point and the paragraph.
+     *
+     * @return the distance, in pixels, between the bullet point and the paragraph.
+     */
+    public int getGapWidth() {
+        return mGapWidth;
+    }
+
+    /**
+     * Get the radius, in pixels, of the bullet point.
+     *
+     * @return the radius, in pixels, of the bullet point.
+     */
+    public int getBulletRadius() {
+        return mBulletRadius;
+    }
+
+    /**
+     * Get the bullet point color.
+     *
+     * @return the bullet point color
+     */
+    public int getColor() {
+        return mColor;
     }
 
     @Override
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout l) {
+    public void drawLeadingMargin(@NonNull Canvas canvas, @NonNull Paint paint, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @Nullable Layout layout) {
         if (((Spanned) text).getSpanStart(this) == start) {
-            Paint.Style style = p.getStyle();
+            Paint.Style style = paint.getStyle();
             int oldcolor = 0;
 
             if (mWantColor) {
-                oldcolor = p.getColor();
-                p.setColor(mColor);
+                oldcolor = paint.getColor();
+                paint.setColor(mColor);
             }
 
-            p.setStyle(Paint.Style.FILL);
+            paint.setStyle(Paint.Style.FILL);
 
-            if (l != null) {
+            if (layout != null) {
                 // "bottom" position might include extra space as a result of line spacing
                 // configuration. Subtract extra space in order to show bullet in the vertical
                 // center of characters.
-                final int line = l.getLineForOffset(start);
-                bottom = bottom - l.getLineExtra(line);
+                final int line = layout.getLineForOffset(start);
+                bottom = bottom - layout.getLineExtra(line);
             }
 
-            final float y = (top + bottom) / 2f;
+            final float yPosition = (top + bottom) / 2f;
+            final float xPosition = x + dir * mBulletRadius;
 
-            if (c.isHardwareAccelerated()) {
-                if (sBulletPath == null) {
-                    sBulletPath = new Path();
-                    sBulletPath.addCircle(0.0f, 0.0f, BULLET_RADIUS, Direction.CW);
+            if (canvas.isHardwareAccelerated()) {
+                if (mBulletPath == null) {
+                    mBulletPath = new Path();
+                    mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
                 }
 
-                c.save();
-                c.translate(x + dir * BULLET_RADIUS, y);
-                c.drawPath(sBulletPath, p);
-                c.restore();
+                canvas.save();
+                canvas.translate(xPosition, yPosition);
+                canvas.drawPath(mBulletPath, paint);
+                canvas.restore();
             } else {
-                c.drawCircle(x + dir * BULLET_RADIUS, y, BULLET_RADIUS, p);
+                canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
             }
 
             if (mWantColor) {
-                p.setColor(oldcolor);
+                paint.setColor(oldcolor);
             }
 
-            p.setStyle(style);
+            paint.setStyle(style);
         }
     }
 }
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 08ab2a1..f7706745 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -27,11 +27,10 @@
  * Changes the color of the text to which the span is attached.
  * <p>
  * For example, to set a green text color you would create a {@link
- * android.text.SpannableStringBuilder} based on the text and set the span.
+ * android.text.SpannableString} based on the text and set the span.
  * <pre>{@code
  * SpannableString string = new SpannableString("Text with a foreground color span");
- *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- * }</pre>
+ *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
  * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
  * <figcaption>Set a text color.</figcaption>
  */
@@ -59,30 +58,29 @@
         mColor = src.readInt();
     }
 
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.FOREGROUND_COLOR_SPAN;
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest  The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written.
-     */
+    @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
+    @Override
     public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 95f048a..3094f27 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -16,56 +16,85 @@
 
 package android.text.style;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Uniformly scales the size of the text to which it's attached by a certain proportion.
+ * <p>
+ * For example, a <code>RelativeSizeSpan</code> that increases the text size by 50% can be
+ * constructed like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with relative size span");
+ *string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" />
+ * <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption>
+ */
 public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final float mProportion;
 
-    public RelativeSizeSpan(float proportion) {
+    /**
+     * Creates a {@link RelativeSizeSpan} based on a proportion.
+     *
+     * @param proportion the proportion with which the text is scaled.
+     */
+    public RelativeSizeSpan(@FloatRange(from = 0) float proportion) {
         mProportion = proportion;
     }
 
-    public RelativeSizeSpan(Parcel src) {
+    /**
+     * Creates a {@link RelativeSizeSpan} from a parcel.
+     */
+    public RelativeSizeSpan(@NonNull Parcel src) {
         mProportion = src.readFloat();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.RELATIVE_SIZE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
+    /**
+     * @return the proportion with which the text size is changed.
+     */
     public float getSizeChange() {
         return mProportion;
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setTextSize(ds.getTextSize() * mProportion);
     }
 
     @Override
-    public void updateMeasureState(TextPaint ds) {
+    public void updateMeasureState(@NonNull TextPaint ds) {
         ds.setTextSize(ds.getTextSize() * mProportion);
     }
 }
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index d0850185..6ef4cec 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -16,45 +16,79 @@
 
 package android.text.style;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Scales horizontally the size of the text to which it's attached by a certain factor.
+ * <p>
+ * Values > 1.0 will stretch the text wider. Values < 1.0 will stretch the text narrower.
+ * <p>
+ * For example, a <code>ScaleXSpan</code> that stretches the text size by 100% can be
+ * constructed like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with ScaleX span");
+ *string.setSpan(new ScaleXSpan(2f), 10, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" />
+ * <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption>
+ */
 public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final float mProportion;
 
-    public ScaleXSpan(float proportion) {
+    /**
+     * Creates a {@link ScaleXSpan} based on a proportion. Values > 1.0 will stretch the text wider.
+     * Values < 1.0 will stretch the text narrower.
+     *
+     * @param proportion the horizontal scale factor.
+     */
+    public ScaleXSpan(@FloatRange(from = 0) float proportion) {
         mProportion = proportion;
     }
 
-    public ScaleXSpan(Parcel src) {
+    /**
+     * Creates a {@link ScaleXSpan} from a parcel.
+     */
+    public ScaleXSpan(@NonNull Parcel src) {
         mProportion = src.readFloat();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SCALE_X_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
+    /**
+     * Get the horizontal scale factor for the text.
+     *
+     * @return the horizontal scale factor.
+     */
     public float getScaleX() {
         return mProportion;
     }
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 1389704..a630505 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -16,42 +16,65 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that strikes through the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with strikethrough span");
+ *string.setSpan(new StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" />
+ * <figcaption>Strikethrough text.</figcaption>
+ */
 public class StrikethroughSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
+
+    /**
+     * Creates a {@link StrikethroughSpan}.
+     */
     public StrikethroughSpan() {
     }
-    
-    public StrikethroughSpan(Parcel src) {
+
+    /**
+     * Creates a {@link StrikethroughSpan} from a parcel.
+     */
+    public StrikethroughSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.STRIKETHROUGH_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setStrikeThruText(true);
     }
 }
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index f1b0d38..3d15aad 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -16,46 +16,74 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * The span that moves the position of the text baseline lower.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("☕- C8H10N4O2\n");
+ *string.setSpan(new SubscriptSpan(), 4, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/subscriptspan.png" />
+ * <figcaption>Text with <code>SubscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the last line of a
+ * TextView, it may appear cut.
+ */
 public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+
+    /**
+     * Creates a {@link SubscriptSpan}.
+     */
     public SubscriptSpan() {
     }
-    
-    public SubscriptSpan(Parcel src) {
+
+    /**
+     * Creates a {@link SubscriptSpan} from a parcel.
+     */
+    public SubscriptSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SUBSCRIPT_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
+    @Override
     public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint tp) {
-        tp.baselineShift -= (int) (tp.ascent() / 2);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
     }
 
     @Override
-    public void updateMeasureState(TextPaint tp) {
-        tp.baselineShift -= (int) (tp.ascent() / 2);
+    public void updateMeasureState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
     }
 }
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index abcf688..3dc9d3f 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -16,46 +16,71 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * The span that moves the position of the text baseline higher.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("1st example");
+ *string.setSpan(new SuperscriptSpan(), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/superscriptspan.png" />
+ * <figcaption>Text with <code>SuperscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the first line of a
+ * TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link
+ * AbsoluteSizeSpan}
+ */
 public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+    /**
+     * Creates a {@link SuperscriptSpan}.
+     */
     public SuperscriptSpan() {
     }
-    
-    public SuperscriptSpan(Parcel src) {
+
+    /**
+     * Creates a {@link SuperscriptSpan} from a parcel.
+     */
+    public SuperscriptSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SUPERSCRIPT_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint tp) {
-        tp.baselineShift += (int) (tp.ascent() / 2);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift += (int) (textPaint.ascent() / 2);
     }
 
     @Override
-    public void updateMeasureState(TextPaint tp) {
-        tp.baselineShift += (int) (tp.ascent() / 2);
+    public void updateMeasureState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift += (int) (textPaint.ascent() / 2);
     }
 }
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 9024dcd..800838e 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -16,42 +16,65 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that underlines the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with underline span");
+ *string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" />
+ * <figcaption>Underlined text.</figcaption>
+ */
 public class UnderlineSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
+
+    /**
+     * Creates an {@link UnderlineSpan}.
+     */
     public UnderlineSpan() {
     }
-    
-    public UnderlineSpan(Parcel src) {
+
+    /**
+     * Creates an {@link UnderlineSpan} from a parcel.
+     */
+    public UnderlineSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.UNDERLINE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setUnderlineText(true);
     }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e94f91a..9f9033c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -43,8 +43,9 @@
         DEFAULT_FLAGS.put("settings_battery_v2", "false");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
-        DEFAULT_FLAGS.put("settings_zone_picker_v2", "false");
+        DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
+        DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
     }
 
     /**
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 1f7f8b9..8830c90 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -283,6 +283,7 @@
      */
     public long getNextFrameNumber() {
         synchronized (mLock) {
+            checkNotReleasedLocked();
             return nativeGetNextFrameNumber(mNativeObject);
         }
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index deb94e81..00860a4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2412,6 +2412,16 @@
         return mProvider.getTextClassifier();
     }
 
+    /**
+     * Returns the {@link ClassLoader} used to load internal WebView classes.
+     * This method is meant for use by the WebView Support Library, there is no reason to use this
+     * method otherwise.
+     */
+    @NonNull
+    public static ClassLoader getWebViewClassLoader() {
+        return getFactory().getWebViewClassLoader();
+    }
+
     //-------------------------------------------------------------------------
     // Interface for WebView providers
     //-------------------------------------------------------------------------
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 3ced6a5..4f7cdab 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -172,4 +172,10 @@
      * @return the singleton WebViewDatabase instance
      */
     WebViewDatabase getWebViewDatabase(Context context);
+
+    /**
+     * Gets the classloader used to load internal WebView implementation classes. This interface
+     * should only be used by the WebView Support Library.
+     */
+    ClassLoader getWebViewClassLoader();
 }
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 6e85ece..0aa2b64 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -158,6 +158,15 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -194,6 +203,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            MediaControlView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            MediaControlView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return MediaControlView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 955f053..56f3dbd 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -34,46 +34,95 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 
+// TODO: Use @link tag to refer MediaPlayer2 in docs once MediaPlayer2.java is submitted. Same to
+// MediaSession2.
+// TODO: change the reference from MediaPlayer to MediaPlayer2.
 /**
- * TODO PUBLIC API
+ * Displays a video file.  VideoView2 class is a View class which is wrapping MediaPlayer2 so that
+ * developers can easily implement a video rendering application.
+ *
+ * <p>
+ * <em> Data sources that VideoView2 supports : </em>
+ * VideoView2 can play video files and audio-only fiels as
+ * well. It can load from various sources such as resources or content providers. The supported
+ * media file formats are the same as MediaPlayer2.
+ *
+ * <p>
+ * <em> View type can be selected : </em>
+ * VideoView2 can render videos on top of TextureView as well as
+ * SurfaceView selectively. The default is SurfaceView and it can be changed using
+ * {@link #setViewType(int)} method. Using SurfaceView is recommended in most cases for saving
+ * battery. TextureView might be preferred for supporting various UIs such as animation and
+ * translucency.
+ *
+ * <p>
+ * <em> Differences between {@link VideoView} class : </em>
+ * VideoView2 covers and inherits the most of
+ * VideoView's functionalities. The main differences are
+ * <ul>
+ * <li> VideoView2 inherits FrameLayout and renders videos using SurfaceView and TextureView
+ * selectively while VideoView inherits SurfaceView class.
+ * <li> VideoView2 is integrated with MediaControlView2 and a default MediaControlView2 instance is
+ * attached to VideoView2 by default. If a developer does not want to use the default
+ * MediaControlView2, needs to set enableControlView attribute to false. For instance,
+ * <pre>
+ * &lt;VideoView2
+ *     android:id="@+id/video_view"
+ *     xmlns:widget="http://schemas.android.com/apk/com.android.media.update"
+ *     widget:enableControlView="false" /&gt;
+ * </pre>
+ * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
+ * to false and assign the customed media control widget using {@link #setMediaControlView2}.
+ * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
+ * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * MediaControlView2 instance.
+ * </p>
+ * </ul>
+ *
+ * <p>
+ * <em> Audio focus and audio attributes : </em>
+ * By default, VideoView2 requests audio focus with
+ * {@link AudioManager#AUDIOFOCUS_GAIN}. Use {@link #setAudioFocusRequest(int)} to change this
+ * behavior. The default {@link AudioAttributes} used during playback have a usage of
+ * {@link AudioAttributes#USAGE_MEDIA} and a content type of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, use {@link #setAudioAttributes(AudioAttributes)} to
+ * modify them.
+ *
+ * <p>
+ * Note: VideoView2 does not retain its full state when going into the background. In particular, it
+ * does not restore the current play state, play position, selected tracks. Applications should save
+ * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
+ * {@link android.app.Activity#onRestoreInstanceState}.
+ *
  * @hide
  */
 public class VideoView2 extends FrameLayout {
+    /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
             VIEW_TYPE_SURFACEVIEW
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewType {}
+
     public static final int VIEW_TYPE_SURFACEVIEW = 1;
     public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
     private final VideoView2Provider mProvider;
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context) {
         this(context, null);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -91,89 +140,102 @@
     }
 
     /**
-     * @hide
+     * Sets MediaControlView2 instance. It will replace the previously assigned MediaControlView2
+     * instance if any.
+     *
+     * @param mediaControlView a media control view2 instance.
      */
     public void setMediaControlView2(MediaControlView2 mediaControlView) {
         mProvider.setMediaControlView2_impl(mediaControlView);
     }
 
     /**
-     * @hide
+     * Returns MediaControlView2 instance which is currently attached to VideoView2 by default or by
+     * {@link #setMediaControlView2} method.
      */
     public MediaControlView2 getMediaControlView2() {
         return mProvider.getMediaControlView2_impl();
     }
 
     /**
-     * @hide
+     * Starts playback with the media contents specified by {@link #setVideoURI} and
+     * {@link #setVideoPath}.
+     * If it has been paused, this method will resume playback from the current position.
      */
     public void start() {
         mProvider.start_impl();
     }
 
     /**
-     * @hide
+     * Pauses playback.
      */
     public void pause() {
         mProvider.pause_impl();
     }
 
     /**
-     * @hide
+     * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
+     * in milliseconds.
      */
     public int getDuration() {
         return mProvider.getDuration_impl();
     }
 
     /**
-     * @hide
+     * Gets current playback position in milliseconds.
      */
     public int getCurrentPosition() {
         return mProvider.getCurrentPosition_impl();
     }
 
+    // TODO: mention about key-frame related behavior.
     /**
-     * @hide
+     * Moves the media by specified time position.
+     * @param msec the offset in milliseconds from the start to seek to.
      */
     public void seekTo(int msec) {
         mProvider.seekTo_impl(msec);
     }
 
     /**
-     * @hide
+     * Says if the media is currently playing.
+     * @return true if the media is playing, false if it is not (eg. paused or stopped).
      */
     public boolean isPlaying() {
         return mProvider.isPlaying_impl();
     }
 
+    // TODO: check what will return if it is a local media.
     /**
-     * @hide
+     * Gets the percentage (0-100) of the content that has been buffered or played so far.
      */
     public int getBufferPercentage() {
         return mProvider.getBufferPercentage_impl();
     }
 
     /**
-     * @hide
+     * Returns the audio session ID.
      */
     public int getAudioSessionId() {
         return mProvider.getAudioSessionId_impl();
     }
 
     /**
-     * @hide
+     * Starts rendering closed caption or subtitles if there is any. The first subtitle track will
+     * be chosen by default if there multiple subtitle tracks exist.
      */
     public void showSubtitle() {
         mProvider.showSubtitle_impl();
     }
 
     /**
-     * @hide
+     * Stops showing closed captions or subtitles.
      */
     public void hideSubtitle() {
         mProvider.hideSubtitle_impl();
     }
 
+    // TODO: This should be revised after integration with MediaPlayer2.
     /**
      * Sets playback speed.
      *
@@ -181,9 +243,7 @@
      * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the
      * maximum speed that internal engine supports, system will determine best handling or it will
      * be reset to the normal speed 1.0f.
-     * TODO: This should be revised after integration with MediaPlayer2.
      * @param speed the playback speed. It should be positive.
-     * @hide
      */
     public void setSpeed(float speed) {
         mProvider.setSpeed_impl(speed);
@@ -194,7 +254,6 @@
      *
      * If setSpeed() has never been called, returns the default value 1.0f.
      * @return current speed setting
-     * @hide
      */
     public float getSpeed() {
         return mProvider.getSpeed_impl();
@@ -213,8 +272,6 @@
      *
      * @param focusGain the type of audio focus gain that will be requested, or
      *     {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during playback.
-     *
-     * @hide
      */
     public void setAudioFocusRequest(int focusGain) {
         mProvider.setAudioFocusRequest_impl(focusGain);
@@ -224,8 +281,6 @@
      * Sets the {@link AudioAttributes} to be used during the playback of the video.
      *
      * @param attributes non-null <code>AudioAttributes</code>.
-     *
-     * @hide
      */
     public void setAudioAttributes(@NonNull AudioAttributes attributes) {
         mProvider.setAudioAttributes_impl(attributes);
@@ -235,35 +290,51 @@
      * Sets video path.
      *
      * @param path the path of the video.
-     * @hide
      */
     public void setVideoPath(String path) {
         mProvider.setVideoPath_impl(path);
     }
 
     /**
-     * @hide
+     * Sets video URI.
+     *
+     * @param uri the URI of the video.
      */
     public void setVideoURI(Uri uri) {
         mProvider.setVideoURI_impl(uri);
     }
 
     /**
-     * @hide
+     * Sets video URI using specific headers.
+     *
+     * @param uri     the URI of the video.
+     * @param headers the headers for the URI request.
+     *                Note that the cross domain redirection is allowed by default, but that can be
+     *                changed with key/value pairs through the headers parameter with
+     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
+     *                to disallow or allow cross domain redirection.
      */
     public void setVideoURI(Uri uri, Map<String, String> headers) {
         mProvider.setVideoURI_impl(uri, headers);
     }
 
     /**
-     * @hide
+     * Selects which view will be used to render video between SurfacView and TextureView.
+     *
+     * @param viewType the view type to render video
+     * <ul>
+     * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+     * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+     * </ul>
      */
     public void setViewType(@ViewType int viewType) {
         mProvider.setViewType_impl(viewType);
     }
 
     /**
-     * @hide
+     * Returns view type.
+     *
+     * @return view type. See {@see setViewType}.
      */
     @ViewType
     public int getViewType() {
@@ -271,42 +342,57 @@
     }
 
     /**
-     * @hide
+     * Stops playback and release all the resources. This should be called whenever a VideoView2
+     * instance is no longer to be used.
      */
     public void stopPlayback() {
         mProvider.stopPlayback_impl();
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the media file is loaded and ready to go.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnPreparedListener(OnPreparedListener l) {
         mProvider.setOnPreparedListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the end of a media file has been reached during
+     * playback.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnCompletionListener(OnCompletionListener l) {
         mProvider.setOnCompletionListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an error occurs during playback or setup.  If no
+     * listener is specified, or if the listener returned false, VideoView2 will inform the user of
+     * any errors.
+     *
+     * @param l The callback that will be run
      */
     public void setOnErrorListener(OnErrorListener l) {
         mProvider.setOnErrorListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an informational event occurs during playback or
+     * setup.
+     *
+     * @param l The callback that will be run
      */
     public void setOnInfoListener(OnInfoListener l) {
         mProvider.setOnInfoListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when a view type change is done.
+     * {@see #setViewType(int)}
+     * @param l The callback that will be run
      */
     public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
         mProvider.setOnViewTypeChangedListener_impl(l);
@@ -314,18 +400,22 @@
 
     /**
      * Interface definition of a callback to be invoked when the viw type has been changed.
-     * @hide
      */
     public interface OnViewTypeChangedListener {
         /**
          * Called when the view type has been changed.
-         * @see VideoView2#setViewType(int)
+         * @see #setViewType(int)
+         * @param viewType
+         * <ul>
+         * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+         * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+         * </ul>
          */
         void onViewTypeChanged(@ViewType int viewType);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when the media source is ready for playback.
      */
     public interface OnPreparedListener {
         /**
@@ -335,7 +425,8 @@
     }
 
     /**
-     * @hide
+     * Interface definition for a callback to be invoked when playback of a media source has
+     * completed.
      */
     public interface OnCompletionListener {
         /**
@@ -345,30 +436,47 @@
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when there has been an error during an
+     * asynchronous operation.
      */
     public interface OnErrorListener {
+        // TODO: Redefine error codes.
         /**
          * Called to indicate an error.
+         * @param what the type of error that has occurred
+         * @param extra an extra code, specific to the error.
+         * @return true if the method handled the error, false if it didn't.
+         * @see MediaPlayer#OnErrorListener
          */
         boolean onError(int what, int extra);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked to communicate some info and/or warning
+     * about the media or its playback.
      */
     public interface OnInfoListener {
         /**
          * Called to indicate an info or a warning.
-         * @see MediaPlayer#OnInfoListener
-         *
          * @param what the type of info or warning.
          * @param extra an extra code, specific to the info.
+         *
+         * @see MediaPlayer#OnInfoListener
          */
         void onInfo(int what, int extra);
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -405,6 +513,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            VideoView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            VideoView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return VideoView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e2d1ad5..d3e807d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -23,6 +23,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.telephony.DataConnectionRealTimeInfo;
@@ -143,6 +144,9 @@
     CellularBatteryStats getCellularBatteryStats();
 
     /** {@hide} */
+    WifiBatteryStats getWifiBatteryStats();
+
+    /** {@hide} */
     GpsBatteryStats getGpsBatteryStats();
 
     HealthStatsParceler takeUidSnapshot(int uid);
diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index ee01a23..d69c7de 100644
--- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -24,4 +24,5 @@
     boolean stopWatchlistLogging();
     void reloadWatchlist();
     void reportWatchlistIfNecessary();
+    byte[] getWatchlistConfigHash();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 799e3e8..b1c45f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -34,6 +34,7 @@
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -131,7 +132,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 174 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -751,6 +752,8 @@
     final StopwatchTimer[] mWifiSignalStrengthsTimer =
             new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
 
+    StopwatchTimer mWifiActiveTimer;
+
     int mBluetoothScanNesting;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected StopwatchTimer mBluetoothScanTimer;
@@ -2785,12 +2788,14 @@
     public static class ControllerActivityCounterImpl extends ControllerActivityCounter
             implements Parcelable {
         private final LongSamplingCounter mIdleTimeMillis;
+        private final LongSamplingCounter mScanTimeMillis;
         private final LongSamplingCounter mRxTimeMillis;
         private final LongSamplingCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase);
+            mScanTimeMillis = new LongSamplingCounter(timeBase);
             mRxTimeMillis = new LongSamplingCounter(timeBase);
             mTxTimeMillis = new LongSamplingCounter[numTxStates];
             for (int i = 0; i < numTxStates; i++) {
@@ -2801,6 +2806,7 @@
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+            mScanTimeMillis = new LongSamplingCounter(timeBase, in);
             mRxTimeMillis = new LongSamplingCounter(timeBase, in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != numTxStates) {
@@ -2816,6 +2822,7 @@
 
         public void readSummaryFromParcel(Parcel in) {
             mIdleTimeMillis.readSummaryFromParcelLocked(in);
+            mScanTimeMillis.readSummaryFromParcelLocked(in);
             mRxTimeMillis.readSummaryFromParcelLocked(in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != mTxTimeMillis.length) {
@@ -2834,6 +2841,7 @@
 
         public void writeSummaryToParcel(Parcel dest) {
             mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+            mScanTimeMillis.writeSummaryFromParcelLocked(dest);
             mRxTimeMillis.writeSummaryFromParcelLocked(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2845,6 +2853,7 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             mIdleTimeMillis.writeToParcel(dest);
+            mScanTimeMillis.writeToParcel(dest);
             mRxTimeMillis.writeToParcel(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2855,6 +2864,7 @@
 
         public void reset(boolean detachIfReset) {
             mIdleTimeMillis.reset(detachIfReset);
+            mScanTimeMillis.reset(detachIfReset);
             mRxTimeMillis.reset(detachIfReset);
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.reset(detachIfReset);
@@ -2864,6 +2874,7 @@
 
         public void detach() {
             mIdleTimeMillis.detach();
+            mScanTimeMillis.detach();
             mRxTimeMillis.detach();
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.detach();
@@ -2881,6 +2892,15 @@
         }
 
         /**
+         * @return a LongSamplingCounter, measuring time spent in the scan state in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter getScanTimeCounter() {
+            return mScanTimeMillis;
+        }
+
+        /**
          * @return a LongSamplingCounter, measuring time spent in the receive state in
          * milliseconds.
          */
@@ -3892,8 +3912,10 @@
         }
         mKernelUidCpuTimeReader.removeUid(isolatedUid);
         mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
-        mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
-        mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+            mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
+            mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+        }
     }
 
     public int mapUid(int uid) {
@@ -5618,8 +5640,11 @@
                     noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid);
                 }
                 mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+                mWifiActiveTimer.startRunningLocked(elapsedRealtime);
             } else {
                 mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+                mWifiActiveTimer.stopRunningLocked(
+                    timestampNs / (1000 * 1000));
             }
             if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
                     + Integer.toHexString(mHistoryCur.states));
@@ -6270,6 +6295,10 @@
         return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
 
+    @Override public long getWifiActiveTime(long elapsedRealtimeUs, int which) {
+        return mWifiActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
     @Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) {
         return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -9916,6 +9945,7 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
                     mOnBatteryTimeBase);
         }
+        mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null, mOnBatteryTimeBase);
         for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null,
                 mOnBatteryTimeBase);
@@ -10609,10 +10639,11 @@
             mWifiSignalStrengthsTimer[i].reset(false);
         }
         mWifiMulticastWakelockTimer.reset(false);
+        mWifiActiveTimer.reset(false);
+        mWifiActivity.reset(false);
         for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i].reset(false);
         }
-        mWifiActivity.reset(false);
         mBluetoothActivity.reset(false);
         mModemActivity.reset(false);
         mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0;
@@ -10875,6 +10906,7 @@
                 // Measured in mAms
                 final long txTimeMs = info.getControllerTxTimeMillis();
                 final long rxTimeMs = info.getControllerRxTimeMillis();
+                final long scanTimeMs = info.getControllerScanTimeMillis();
                 final long idleTimeMs = info.getControllerIdleTimeMillis();
                 final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
 
@@ -10887,6 +10919,7 @@
                     Slog.d(TAG, "  Rx Time:    " + rxTimeMs + " ms");
                     Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
                     Slog.d(TAG, "  Total Time: " + totalTimeMs + " ms");
+                    Slog.d(TAG, "  Scan Time:  " + scanTimeMs + " ms");
                 }
 
                 long totalWifiLockTimeMs = 0;
@@ -11020,6 +11053,8 @@
                 mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
                 mWifiActivity.getTxTimeCounters()[0].addCountLocked(
                         info.getControllerTxTimeMillis());
+                mWifiActivity.getScanTimeCounter().addCountLocked(
+                    info.getControllerScanTimeMillis());
                 mWifiActivity.getIdleTimeCounter().addCountLocked(
                         info.getControllerIdleTimeMillis());
 
@@ -11063,6 +11098,39 @@
                 return;
             }
 
+            if (activityInfo != null) {
+                mHasModemReporting = true;
+                mModemActivity.getIdleTimeCounter().addCountLocked(
+                    activityInfo.getIdleTimeMillis());
+                mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                    mModemActivity.getTxTimeCounters()[lvl]
+                        .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+                }
+
+                // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+                final double opVolt = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+                if (opVolt != 0) {
+                    double energyUsed =
+                        activityInfo.getSleepTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
+                            + activityInfo.getIdleTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+                            + activityInfo.getRxTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+                    int[] txCurrentMa = activityInfo.getTxTimeMillis();
+                    for (int i = 0; i < Math.min(txCurrentMa.length,
+                        SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+                        energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower(
+                            PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+                    }
+
+                    // We store the power drain as mAms.
+                    mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+                }
+            }
+
             final long elapsedRealtimeMs = mClocks.elapsedRealtime();
             long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
@@ -11161,26 +11229,6 @@
                 mNetworkStatsPool.release(delta);
                 delta = null;
             }
-
-            if (activityInfo != null) {
-                mHasModemReporting = true;
-                mModemActivity.getIdleTimeCounter().addCountLocked(
-                        activityInfo.getIdleTimeMillis());
-                mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
-                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
-                    mModemActivity.getTxTimeCounters()[lvl]
-                            .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
-                }
-
-                // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
-                final double opVolt = mPowerProfile.getAveragePower(
-                        PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
-                if (opVolt != 0) {
-                    // We store the power drain as mAms.
-                    mModemActivity.getPowerCounter().addCountLocked(
-                            (long) (activityInfo.getEnergyUsed() / opVolt));
-                }
-            }
         }
     }
 
@@ -11538,8 +11586,10 @@
         if (!mOnBatteryInternal) {
             mKernelUidCpuTimeReader.readDelta(null);
             mKernelUidCpuFreqTimeReader.readDelta(null);
-            mKernelUidCpuActiveTimeReader.readDelta(null);
-            mKernelUidCpuClusterTimeReader.readDelta(null);
+            if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                mKernelUidCpuActiveTimeReader.readDelta(null);
+                mKernelUidCpuClusterTimeReader.readDelta(null);
+            }
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
             }
@@ -11556,8 +11606,10 @@
             updateClusterSpeedTimes(updatedUids);
         }
         readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
-        readKernelUidCpuActiveTimesLocked();
-        readKernelUidCpuClusterTimesLocked();
+        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+            readKernelUidCpuActiveTimesLocked();
+            readKernelUidCpuClusterTimesLocked();
+        }
     }
 
     /**
@@ -12530,6 +12582,56 @@
         return s;
     }
 
+     /*@hide */
+     public WifiBatteryStats getWifiBatteryStats() {
+         WifiBatteryStats s = new WifiBatteryStats();
+         final int which = STATS_SINCE_CHARGED;
+         final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+         final ControllerActivityCounter counter = getWifiControllerActivity();
+         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+         final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+         final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+         final long totalControllerActivityTimeMs
+             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+         final long sleepTimeMs
+             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+         long numAppScanRequest = 0;
+         for (int i = 0; i < mUidStats.size(); i++) {
+             numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+         }
+         long[] timeInStateMs = new long[NUM_WIFI_STATES];
+         for (int i=0; i<NUM_WIFI_STATES; i++) {
+            timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
+         }
+         long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+             timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+         }
+         long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+             timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+         }
+         s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+         s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+         s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+         s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+         s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+         s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+         s.setSleepTimeMs(sleepTimeMs);
+         s.setIdleTimeMs(idleTimeMs);
+         s.setRxTimeMs(rxTimeMs);
+         s.setTxTimeMs(txTimeMs);
+         s.setScanTimeMs(scanTimeMs);
+         s.setEnergyConsumedMaMs(energyConsumedMaMs);
+         s.setNumAppScanRequest(numAppScanRequest);
+         s.setTimeInStateMs(timeInStateMs);
+         s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+         s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+         return s;
+     }
+
     /*@hide */
     public GpsBatteryStats getGpsBatteryStats() {
         GpsBatteryStats s = new GpsBatteryStats();
@@ -12804,10 +12906,19 @@
     public final class Constants extends ContentObserver {
         public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE
                 = "track_cpu_times_by_proc_state";
+        public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
+                = "track_cpu_active_cluster_time";
+        public static final String KEY_READ_BINARY_CPU_TIME
+                = "read_binary_cpu_time";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+        private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
+        private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
+        public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
+        // Not used right now.
+        public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -12843,6 +12954,11 @@
                 updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE,
                         mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE,
                                 DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
+                TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
+                        KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
+                READ_BINARY_CPU_TIME = mParser.getBoolean(
+                        KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
+
             }
         }
 
@@ -12857,6 +12973,10 @@
         public void dumpLocked(PrintWriter pw) {
             pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
             pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
+            pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
+            pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
+            pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
+            pw.println(READ_BINARY_CPU_TIME);
         }
     }
 
@@ -13222,10 +13342,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
         }
+        mWifiActiveTimer.readSummaryFromParcelLocked(in);
+        mWifiActivity.readSummaryFromParcel(in);
         for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in);
         }
-        mWifiActivity.readSummaryFromParcel(in);
         mBluetoothActivity.readSummaryFromParcel(in);
         mModemActivity.readSummaryFromParcel(in);
         mHasWifiReporting = in.readInt() != 0;
@@ -13667,10 +13788,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
+        mWifiActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mWifiActivity.writeSummaryToParcel(out);
         for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
-        mWifiActivity.writeSummaryToParcel(out);
         mBluetoothActivity.writeSummaryToParcel(out);
         mModemActivity.writeSummaryToParcel(out);
         out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -14145,12 +14267,14 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i,
                     null, mOnBatteryTimeBase, in);
         }
+        mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null,
+            mOnBatteryTimeBase, in);
+        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                NUM_WIFI_TX_LEVELS, in);
         for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i,
                 null, mOnBatteryTimeBase, in);
         }
-        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
-                NUM_WIFI_TX_LEVELS, in);
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -14348,10 +14472,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
         }
+        mWifiActiveTimer.writeToParcel(out, uSecRealtime);
+        mWifiActivity.writeToParcel(out, 0);
         for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
         }
-        mWifiActivity.writeToParcel(out, 0);
         mBluetoothActivity.writeToParcel(out, 0);
         mModemActivity.writeToParcel(out, 0);
         out.writeInt(mHasWifiReporting ? 1 : 0);
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index fcbbcd0..240fc51 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -97,6 +97,7 @@
     public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
             "bluetooth.controller.voltage";
 
+    public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep";
     public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
     public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
     public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
@@ -296,10 +297,6 @@
                 com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
-                com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
-                com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
-                com.android.internal.R.integer.config_wifi_tx_cur_ma,
-                com.android.internal.R.integer.config_wifi_operating_voltage_mv,
         };
 
         String[] configResIdKeys = new String[]{
@@ -307,10 +304,6 @@
                 POWER_BLUETOOTH_CONTROLLER_RX,
                 POWER_BLUETOOTH_CONTROLLER_TX,
                 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
-                POWER_WIFI_CONTROLLER_IDLE,
-                POWER_WIFI_CONTROLLER_RX,
-                POWER_WIFI_CONTROLLER_TX,
-                POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
         };
 
         for (int i = 0; i < configResIds.length; i++) {
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
new file mode 100644
index 0000000..245a66e
--- /dev/null
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os.logging;
+
+import android.content.Context;
+import android.util.StatsLog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Used to wrap different logging calls in one, so that client side code base is clean and more
+ * readable.
+ */
+public class MetricsLoggerWrapper {
+
+    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
+    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
+
+    public static void logPictureInPictureDismissByTap(Context context) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_TAP);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureDismissByDrag(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_DRAG);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureMinimize(Context context, boolean isMinimized) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
+                isMinimized);
+        if (isMinimized) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
+        } else {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+        }
+    }
+
+    public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
+        MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
+                menuStateFull);
+    }
+
+    public static void logPictureInPictureEnter(Context context,
+            boolean supportsEnterPipOnTaskSwitch) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
+                supportsEnterPipOnTaskSwitch);
+        if (supportsEnterPipOnTaskSwitch) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
+        }
+    }
+
+    public static void logPictureInPictureFullScreen(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+    }
+}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 33977f3..5577d6e 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -89,9 +89,12 @@
         mAvatarView = findViewById(R.id.message_icon);
     }
 
-    public void setSender(Notification.Person sender) {
+    public void setSender(Notification.Person sender, CharSequence nameOverride) {
         mSender = sender;
-        mSenderName.setText(sender.getName());
+        if (nameOverride == null) {
+            nameOverride = sender.getName();
+        }
+        mSenderName.setText(nameOverride);
         mNeedsGeneratedAvatar = sender.getIcon() == null;
         if (!mNeedsGeneratedAvatar) {
             setAvatar(sender.getIcon());
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 7a64cad..d45c086 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -80,6 +80,7 @@
     private boolean mIsOneToOne;
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
     private Notification.Person mUser;
+    private CharSequence mNameReplacement;
 
     public MessagingLayout(@NonNull Context context) {
         super(context);
@@ -121,6 +122,11 @@
     }
 
     @RemotableViewMethod
+    public void setNameReplacement(CharSequence nameReplacement) {
+        mNameReplacement = nameReplacement;
+    }
+
+    @RemotableViewMethod
     public void setData(Bundle extras) {
         Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
         List<Notification.MessagingStyle.Message> newMessages
@@ -326,7 +332,12 @@
                 mAddedGroups.add(newGroup);
             }
             newGroup.setLayoutColor(mLayoutColor);
-            newGroup.setSender(senders.get(groupIndex));
+            Notification.Person sender = senders.get(groupIndex);
+            CharSequence nameOverride = null;
+            if (sender != mUser && mNameReplacement != null) {
+                nameOverride = mNameReplacement;
+            }
+            newGroup.setSender(sender, nameOverride);
             mGroups.add(newGroup);
 
             if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) {
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 12feaab..ec15cce 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -58,8 +58,7 @@
         SkPictureRecorder recorder;
         SkCanvas* skcanvas = recorder.beginRecording(bounds);
         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
-        postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(),
-                              bounds.height());
+        postProcessAndRelease(env, jpostProcess, std::move(canvas));
         if (env->ExceptionCheck()) {
             return 0;
         }
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
index 115edd4..85c9ef3 100644
--- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -67,7 +67,7 @@
         }
 
         if (!buffer) {
-            return this->setPosition(mPosition + size);
+            return this->setPosition(mPosition + size) ? size : 0;
         }
 
         auto* env = get_env_or_die(mJvm);
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 1c2528f..743a7ef 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -37,15 +37,13 @@
 using namespace android;
 
 static jclass    gImageDecoder_class;
-static jclass    gPoint_class;
+static jclass    gSize_class;
 static jclass    gIncomplete_class;
-static jclass    gCorrupt_class;
 static jclass    gCanvas_class;
 static jmethodID gImageDecoder_constructorMethodID;
 static jmethodID gImageDecoder_postProcessMethodID;
-static jmethodID gPoint_constructorMethodID;
+static jmethodID gSize_constructorMethodID;
 static jmethodID gIncomplete_constructorMethodID;
-static jmethodID gCorrupt_constructorMethodID;
 static jmethodID gCallback_onPartialImageMethodID;
 static jmethodID gCanvas_constructorMethodID;
 static jmethodID gCanvas_releaseMethodID;
@@ -157,8 +155,7 @@
     return native_create(env, std::move(stream));
 }
 
-jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
-                           int width, int height) {
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
     jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
                                      reinterpret_cast<jlong>(canvas.get()));
     if (!jcanvas) {
@@ -169,8 +166,7 @@
     // jcanvas now owns canvas.
     canvas.release();
 
-    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID,
-                              jcanvas, width, height);
+    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
 }
 
 // Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
@@ -271,39 +267,47 @@
     if (jexception) {
         env->ExceptionClear();
     }
+    int onPartialImageError = jexception ? 1  // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+                                         : 0; // No error.
     switch (result) {
         case SkCodec::kSuccess:
             // Ignore the exception, since the decode was successful anyway.
             jexception = nullptr;
+            onPartialImageError = 0;
             break;
         case SkCodec::kIncompleteInput:
-            if (jcallback && !jexception) {
-                jexception = (jthrowable) env->NewObject(gIncomplete_class,
-                                                         gIncomplete_constructorMethodID);
+            if (!jexception) {
+                onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
             }
             break;
         case SkCodec::kErrorInInput:
-            if (jcallback && !jexception) {
-                jexception = (jthrowable) env->NewObject(gCorrupt_class,
-                                                         gCorrupt_constructorMethodID);
+            if (!jexception) {
+                onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR
             }
             break;
         default:
             SkString msg;
-            msg.printf("getPixels failed with error %i", result);
+            msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result));
             doThrowIOE(env, msg.c_str());
             return nullptr;
     }
 
-    if (jexception) {
-        bool throwException = !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
-                                                      jexception);
+    if (jexception || onPartialImageError) {
+        bool throwException = !jcallback ||
+            !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
+                                    onPartialImageError);
         if (env->ExceptionCheck()) {
             return nullptr;
         }
 
         if (throwException) {
-            env->Throw(jexception);
+            if (jexception) {
+                env->Throw(jexception);
+            } else if (onPartialImageError == 2) {
+                env->ThrowNew(gIncomplete_class, "Incomplete input");
+            } else {
+                doThrowIOE(env, "image has an error!");
+            }
             return nullptr;
         }
     }
@@ -399,8 +403,7 @@
     if (jpostProcess) {
         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
 
-        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas),
-                                                 bm.width(), bm.height());
+        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas));
         if (env->ExceptionCheck()) {
             return nullptr;
         }
@@ -472,7 +475,7 @@
                                             jint sampleSize) {
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
     SkISize size = decoder->mCodec->getSampledDimensions(sampleSize);
-    return env->NewObject(gPoint_class, gPoint_constructorMethodID, size.width(), size.height());
+    return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height());
 }
 
 static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
@@ -496,9 +499,9 @@
     { "nCreate",        "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
     { "nCreate",        "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
     { "nCreate",        "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
-    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
-    { "nGetSampledSize","(JI)Landroid/graphics/Point;",          (void*) ImageDecoder_nGetSampledSize },
+    { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
     { "nClose",         "(J)V",                                  (void*) ImageDecoder_nClose},
     { "nGetMimeType",   "(J)Ljava/lang/String;",                 (void*) ImageDecoder_nGetMimeType },
@@ -507,19 +510,15 @@
 int register_android_graphics_ImageDecoder(JNIEnv* env) {
     gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
     gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V");
-    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I");
+    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
 
-    gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
-    gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V");
+    gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
+    gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
 
     gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException"));
     gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V");
 
-    gCorrupt_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$CorruptException"));
-    gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V");
-
-    jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener");
-    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z");
+    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(I)Z");
 
     gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
     gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h
index 2df71eb..5d7e676 100644
--- a/core/jni/android/graphics/ImageDecoder.h
+++ b/core/jni/android/graphics/ImageDecoder.h
@@ -51,5 +51,4 @@
 // Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
 // releases the Canvas.
 // Caller needs to check for exceptions.
-jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
-                           int width, int height);
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas);
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index eefcb74..34544d3 100644
--- a/core/jni/android_database_SQLiteCommon.cpp
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -18,8 +18,108 @@
 
 #include <utils/String8.h>
 
+#include <map>
+
 namespace android {
 
+static const std::map<int, std::string> sErrorCodesMap = {
+    // Primary Result Code List
+    {4,     "SQLITE_ABORT"},
+    {23,    "SQLITE_AUTH"},
+    {5,     "SQLITE_BUSY"},
+    {14,    "SQLITE_CANTOPEN"},
+    {19,    "SQLITE_CONSTRAINT"},
+    {11,    "SQLITE_CORRUPT"},
+    {101,   "SQLITE_DONE"},
+    {16,    "SQLITE_EMPTY"},
+    {1,     "SQLITE_ERROR"},
+    {24,    "SQLITE_FORMAT"},
+    {13,    "SQLITE_FULL"},
+    {2,     "SQLITE_INTERNAL"},
+    {9,     "SQLITE_INTERRUPT"},
+    {10,    "SQLITE_IOERR"},
+    {6,     "SQLITE_LOCKED"},
+    {20,    "SQLITE_MISMATCH"},
+    {21,    "SQLITE_MISUSE"},
+    {22,    "SQLITE_NOLFS"},
+    {7,     "SQLITE_NOMEM"},
+    {26,    "SQLITE_NOTADB"},
+    {12,    "SQLITE_NOTFOUND"},
+    {27,    "SQLITE_NOTICE"},
+    {0,     "SQLITE_OK"},
+    {3,     "SQLITE_PERM"},
+    {15,    "SQLITE_PROTOCOL"},
+    {25,    "SQLITE_RANGE"},
+    {8,     "SQLITE_READONLY"},
+    {100,   "SQLITE_ROW"},
+    {17,    "SQLITE_SCHEMA"},
+    {18,    "SQLITE_TOOBIG"},
+    {28,    "SQLITE_WARNING"},
+    // Extended Result Code List
+    {516,   "SQLITE_ABORT_ROLLBACK"},
+    {261,   "SQLITE_BUSY_RECOVERY"},
+    {517,   "SQLITE_BUSY_SNAPSHOT"},
+    {1038,  "SQLITE_CANTOPEN_CONVPATH"},
+    {782,   "SQLITE_CANTOPEN_FULLPATH"},
+    {526,   "SQLITE_CANTOPEN_ISDIR"},
+    {270,   "SQLITE_CANTOPEN_NOTEMPDIR"},
+    {275,   "SQLITE_CONSTRAINT_CHECK"},
+    {531,   "SQLITE_CONSTRAINT_COMMITHOOK"},
+    {787,   "SQLITE_CONSTRAINT_FOREIGNKEY"},
+    {1043,  "SQLITE_CONSTRAINT_FUNCTION"},
+    {1299,  "SQLITE_CONSTRAINT_NOTNULL"},
+    {1555,  "SQLITE_CONSTRAINT_PRIMARYKEY"},
+    {2579,  "SQLITE_CONSTRAINT_ROWID"},
+    {1811,  "SQLITE_CONSTRAINT_TRIGGER"},
+    {2067,  "SQLITE_CONSTRAINT_UNIQUE"},
+    {2323,  "SQLITE_CONSTRAINT_VTAB"},
+    {267,   "SQLITE_CORRUPT_VTAB"},
+    {3338,  "SQLITE_IOERR_ACCESS"},
+    {2826,  "SQLITE_IOERR_BLOCKED"},
+    {3594,  "SQLITE_IOERR_CHECKRESERVEDLOCK"},
+    {4106,  "SQLITE_IOERR_CLOSE"},
+    {6666,  "SQLITE_IOERR_CONVPATH"},
+    {2570,  "SQLITE_IOERR_DELETE"},
+    {5898,  "SQLITE_IOERR_DELETE_NOENT"},
+    {4362,  "SQLITE_IOERR_DIR_CLOSE"},
+    {1290,  "SQLITE_IOERR_DIR_FSYNC"},
+    {1802,  "SQLITE_IOERR_FSTAT"},
+    {1034,  "SQLITE_IOERR_FSYNC"},
+    {6410,  "SQLITE_IOERR_GETTEMPPATH"},
+    {3850,  "SQLITE_IOERR_LOCK"},
+    {6154,  "SQLITE_IOERR_MMAP"},
+    {3082,  "SQLITE_IOERR_NOMEM"},
+    {2314,  "SQLITE_IOERR_RDLOCK"},
+    {266,   "SQLITE_IOERR_READ"},
+    {5642,  "SQLITE_IOERR_SEEK"},
+    {5130,  "SQLITE_IOERR_SHMLOCK"},
+    {5386,  "SQLITE_IOERR_SHMMAP"},
+    {4618,  "SQLITE_IOERR_SHMOPEN"},
+    {4874,  "SQLITE_IOERR_SHMSIZE"},
+    {522,   "SQLITE_IOERR_SHORT_READ"},
+    {1546,  "SQLITE_IOERR_TRUNCATE"},
+    {2058,  "SQLITE_IOERR_UNLOCK"},
+    {778,   "SQLITE_IOERR_WRITE"},
+    {262,   "SQLITE_LOCKED_SHAREDCACHE"},
+    {539,   "SQLITE_NOTICE_RECOVER_ROLLBACK"},
+    {283,   "SQLITE_NOTICE_RECOVER_WAL"},
+    {256,   "SQLITE_OK_LOAD_PERMANENTLY"},
+    {520,   "SQLITE_READONLY_CANTLOCK"},
+    {1032,  "SQLITE_READONLY_DBMOVED"},
+    {264,   "SQLITE_READONLY_RECOVERY"},
+    {776,   "SQLITE_READONLY_ROLLBACK"},
+    {284,   "SQLITE_WARNING_AUTOINDEX"},
+};
+
+static std::string sqlite3_error_code_to_msg(int errcode) {
+    auto it = sErrorCodesMap.find(errcode);
+    if (it != sErrorCodesMap.end()) {
+        return std::to_string(errcode) + " " + it->second;
+    } else {
+        return std::to_string(errcode);
+    }
+}
+
 /* throw a SQLiteException with a message appropriate for the error in handle */
 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
     throw_sqlite3_exception(env, handle, NULL);
@@ -123,7 +223,8 @@
     if (sqlite3Message) {
         String8 fullMessage;
         fullMessage.append(sqlite3Message);
-        fullMessage.appendFormat(" (code %d)", errcode); // print extended error code
+        const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str();
+        fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code
         if (message) {
             fullMessage.append(": ");
             fullMessage.append(message);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 51cefb9..c79f5bd 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -35,6 +35,7 @@
 #define ENCODING_DOLBY_TRUEHD   14
 #define ENCODING_AAC_ELD        15
 #define ENCODING_AAC_XHE        16
+#define ENCODING_AC4            17
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -77,6 +78,8 @@
         return AUDIO_FORMAT_AAC_ELD;
     case ENCODING_AAC_XHE:
         return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
+    case ENCODING_AC4:
+        return AUDIO_FORMAT_AC4;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
     default:
@@ -125,6 +128,8 @@
     // FIXME needs addition of AUDIO_FORMAT_AAC_XHE
     //case AUDIO_FORMAT_AAC_XHE:
     //    return ENCODING_AAC_XHE;
+    case AUDIO_FORMAT_AC4:
+        return ENCODING_AC4;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 8d6df12..95eb889 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -391,8 +391,9 @@
     optional SettingProto zram_enabled = 347;
     optional SettingProto enable_smart_replies_in_notifications = 348;
     optional SettingProto show_first_crash_dialog = 349;
+    optional SettingProto wifi_connected_mac_randomization_enabled = 350;
 
-    // Next tag = 350;
+    // Next tag = 351;
 }
 
 message SecureSettingsProto {
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf7..c9f7d52 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@
 
   // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
   repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+  // Whether device is a small battery device
+  optional bool is_small_battery_device = 6;
+
+  // Whether force app standby for small battery device setting is enabled
+  optional bool force_all_apps_standby_for_small_battery = 7;
+
+  // Whether device is charging
+  optional bool is_charging = 8;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ba30981..93d852c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2313,6 +2313,11 @@
     <permission android:name="android.permission.RECOVERY"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to read system update info.
+         @hide -->
+    <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
+        android:protectionLevel="signature" />
+
     <!-- Allows the system to bind to an application's task services
          @hide -->
     <permission android:name="android.permission.BIND_JOB_SERVICE"
@@ -2843,6 +2848,14 @@
     <permission android:name="android.permission.INSTALL_SELF_UPDATES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to install updates. This is a limited version
+         of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+    <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
index 3074b28..24dd4d8 100644
--- a/core/res/res/drawable/ic_screenshot.xml
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -17,10 +17,8 @@
     android:width="24.0dp"
     android:height="24.0dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:pathData="M0,0h24v24H0V0z"
-        android:fillColor="#00000000"/>
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
     <path
         android:fillColor="#FF000000"
         android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 64f291c..c623c9a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -645,19 +645,7 @@
 
     <!-- Wifi driver supports IEEE80211AC for softap -->
     <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
-
-    <!-- Idle Receive current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
-
-    <!-- Rx current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_active_rx_cur_ma">0</integer>
-
-    <!-- Tx current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_tx_cur_ma">0</integer>
-
-    <!-- Operating volatage for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_operating_voltage_mv">0</integer>
-
+    
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0c844c9..170ba42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4821,4 +4821,7 @@
     <!-- Text describing a permission request for one app to show another app's
          slices [CHAR LIMIT=NONE] -->
     <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
+
+    <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
+    <string name="screenshot_edit">Edit</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 03a800d..4ef0a6c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -384,10 +384,6 @@
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
   <java-symbol type="integer"  name="config_wifi_network_switching_blacklist_time" />
-  <java-symbol type="integer"  name="config_wifi_idle_receive_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_active_rx_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_tx_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_operating_voltage_mv" />
   <java-symbol type="string"  name="config_wifi_framework_sap_2G_channel_list" />
   <java-symbol type="integer" name="config_wifi_framework_max_tx_rate_for_full_scan" />
   <java-symbol type="integer" name="config_wifi_framework_max_rx_rate_for_full_scan" />
@@ -3227,5 +3223,8 @@
   <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
 
   <java-symbol type="bool" name="config_supportBluetoothPersistedState" />
+
   <java-symbol type="string" name="slices_permission_request" />
+
+  <java-symbol type="string" name="screenshot_edit" />
 </resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 6e31cd2..bc4b10f 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,15 +51,6 @@
       <value>0.1</value> <!-- ~1mA -->
   </array>
 
-
-  <!-- Radio related values. For modems WITH energy reporting support in firmware, use
-       modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage.
-       -->
-  <item name="modem.controller.idle">0</item>
-  <item name="modem.controller.rx">0</item>
-  <item name="modem.controller.tx">0</item>
-  <item name="modem.controller.voltage">0</item>
-
   <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
        number of CPU cores for that cluster.
 
@@ -123,4 +114,17 @@
     <value>2</value>    <!-- 4097-/hr -->
   </array>
 
+  <!-- Cellular modem related values. Default is 0.-->
+  <item name="modem.controller.sleep">0</item>
+  <item name="modem.controller.idle">0</item>
+  <item name="modem.controller.rx">0</item>
+  <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+  </array>
+  <item name="modem.controller.voltage">0</item>
+
 </device>
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index 4b84429..584257b 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -24,7 +24,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.ApkLite;
 import android.content.pm.PackageParser.Package;
+import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.os.FileUtils;
 import android.support.test.InstrumentationRegistry;
@@ -34,6 +36,7 @@
 import com.android.frameworks.coretests.R;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,6 +48,7 @@
 import libcore.io.IoUtils;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -201,6 +205,31 @@
         }
     }
 
+    @Test
+    public void testPackageSizeWithDmFile()
+            throws IOException, PackageParserException {
+        copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+        File dm = createDexMetadataFile("install_split_base.apk");
+        PackageParser.PackageLite pkg = new PackageParser().parsePackageLite(mTmpDir,
+                0 /* flags */);
+
+        Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
+    }
+
+    // This simulates the 'adb shell pm install' flow.
+    @Test
+    public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
+        File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
+        File dm = createDexMetadataFile("install_split_base.apk");
+        try (FileInputStream is = new FileInputStream(base)) {
+            ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
+            PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+                    null, null);
+            Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
+        }
+
+    }
+
     private static boolean isDexMetadataForApk(String dmaPath, String apkPath) {
         return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals(
                 dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length()));
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ec3a6ce..7403c26 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -213,6 +213,7 @@
                     Settings.Global.FANCY_IME_ANIMATIONS,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
                     Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                     Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
                     Settings.Global.GLOBAL_HTTP_PROXY_HOST,
@@ -390,6 +391,7 @@
                     Settings.Global.WFC_IMS_ROAMING_MODE,
                     Settings.Global.WIFI_BADGING_THRESHOLDS,
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
                     Settings.Global.WIFI_COUNTRY_CODE,
                     Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
                     Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 9166438..6fe19a2 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -72,26 +72,26 @@
         final LongitudinalReportingEncoder encoder =
                 LongitudinalReportingEncoder.createInsecureEncoderForTest(
                         config);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(0, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
-        assertEquals(0, encoder.encodeBoolean(true)[0]);
-        assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
 
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
-        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
-        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
 
         // Test if IRR returns original result when f = 0
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index dad98b8..fa0343d 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -80,7 +80,7 @@
         int numBits = 8;
         final long inputValue = 254L;
         final long prrValue = 250L;
-        final long prrAndIrrValue = 184L;
+        final long prrAndIrrValue = 244L;
 
         final RapporConfig config1 = new RapporConfig(
                 "Foo", // encoderId
diff --git a/docs/html/reference/images/text/style/absolutesizespan.png b/docs/html/reference/images/text/style/absolutesizespan.png
new file mode 100644
index 0000000..40d5a79
--- /dev/null
+++ b/docs/html/reference/images/text/style/absolutesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/custombulletspan.png b/docs/html/reference/images/text/style/custombulletspan.png
new file mode 100644
index 0000000..251f8a1
--- /dev/null
+++ b/docs/html/reference/images/text/style/custombulletspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/defaultbulletspan.png b/docs/html/reference/images/text/style/defaultbulletspan.png
new file mode 100644
index 0000000..854143f
--- /dev/null
+++ b/docs/html/reference/images/text/style/defaultbulletspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/relativesizespan.png b/docs/html/reference/images/text/style/relativesizespan.png
new file mode 100644
index 0000000..eaca5ad
--- /dev/null
+++ b/docs/html/reference/images/text/style/relativesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/scalexspan.png b/docs/html/reference/images/text/style/scalexspan.png
new file mode 100644
index 0000000..a5ca26f
--- /dev/null
+++ b/docs/html/reference/images/text/style/scalexspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/strikethroughspan.png b/docs/html/reference/images/text/style/strikethroughspan.png
new file mode 100644
index 0000000..a49ecad
--- /dev/null
+++ b/docs/html/reference/images/text/style/strikethroughspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/subscriptspan.png b/docs/html/reference/images/text/style/subscriptspan.png
new file mode 100644
index 0000000..aac7092
--- /dev/null
+++ b/docs/html/reference/images/text/style/subscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/superscriptspan.png b/docs/html/reference/images/text/style/superscriptspan.png
new file mode 100644
index 0000000..996f59d
--- /dev/null
+++ b/docs/html/reference/images/text/style/superscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/underlinespan.png b/docs/html/reference/images/text/style/underlinespan.png
new file mode 100644
index 0000000..dbcd0d9
--- /dev/null
+++ b/docs/html/reference/images/text/style/underlinespan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 4d715d1..02d22be 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import static android.system.OsConstants.SEEK_CUR;
 import static android.system.OsConstants.SEEK_SET;
 
 import android.annotation.IntDef;
@@ -31,6 +32,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
+import android.util.Size;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.DisplayMetrics;
@@ -40,6 +42,7 @@
 import dalvik.system.CloseGuard;
 
 import java.nio.ByteBuffer;
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -55,14 +58,16 @@
 
 /**
  *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
- *  @hide
  */
 public final class ImageDecoder implements AutoCloseable {
     /**
      *  Source of the encoded image data.
      */
     public static abstract class Source {
+        private Source() {}
+
         /* @hide */
+        @Nullable
         Resources getResources() { return null; }
 
         /* @hide */
@@ -79,11 +84,12 @@
         }
 
         /* @hide */
+        @NonNull
         abstract ImageDecoder createImageDecoder() throws IOException;
     };
 
     private static class ByteArraySource extends Source {
-        ByteArraySource(byte[] data, int offset, int length) {
+        ByteArraySource(@NonNull byte[] data, int offset, int length) {
             mData = data;
             mOffset = offset;
             mLength = length;
@@ -99,7 +105,7 @@
     }
 
     private static class ByteBufferSource extends Source {
-        ByteBufferSource(ByteBuffer buffer) {
+        ByteBufferSource(@NonNull ByteBuffer buffer) {
             mBuffer = buffer;
         }
         private final ByteBuffer mBuffer;
@@ -116,7 +122,7 @@
     }
 
     private static class ContentResolverSource extends Source {
-        ContentResolverSource(ContentResolver resolver, Uri uri) {
+        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
             mResolver = resolver;
             mUri = uri;
         }
@@ -167,7 +173,31 @@
         }
     }
 
-    private static ImageDecoder createFromStream(InputStream is) throws IOException {
+    @NonNull
+    private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
+        FileInputStream stream = new FileInputStream(file);
+        FileDescriptor fd = stream.getFD();
+        try {
+            Os.lseek(fd, 0, SEEK_CUR);
+        } catch (ErrnoException e) {
+            return createFromStream(stream);
+        }
+
+        ImageDecoder decoder = null;
+        try {
+            decoder = nCreate(fd);
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(stream);
+            } else {
+                decoder.mInputStream = stream;
+            }
+        }
+        return decoder;
+    }
+
+    @NonNull
+    private static ImageDecoder createFromStream(@NonNull InputStream is) throws IOException {
         // Arbitrary size matches BitmapFactory.
         byte[] storage = new byte[16 * 1024];
         ImageDecoder decoder = null;
@@ -220,7 +250,7 @@
     }
 
     private static class ResourceSource extends Source {
-        ResourceSource(Resources res, int resId) {
+        ResourceSource(@NonNull Resources res, int resId) {
             mResources = res;
             mResId = resId;
             mResDensity = Bitmap.DENSITY_NONE;
@@ -269,59 +299,54 @@
         }
     }
 
+    private static class FileSource extends Source {
+        FileSource(@NonNull File file) {
+            mFile = file;
+        }
+
+        private final File mFile;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return createFromFile(mFile);
+        }
+    }
+
     /**
      *  Contains information about the encoded image.
      */
     public static class ImageInfo {
-        /**
-         * Width of the image, without scaling or cropping.
-         */
-        public final int width;
+        private final Size mSize;
+        private ImageDecoder mDecoder;
 
-        /**
-         * Height of the image, without scaling or cropping.
-         */
-        public final int height;
-
-        /* @hide */
-        ImageDecoder decoder;
-
-        /* @hide */
-        ImageInfo(ImageDecoder decoder) {
-            this.width   = decoder.mWidth;
-            this.height  = decoder.mHeight;
-            this.decoder = decoder;
+        private ImageInfo(@NonNull ImageDecoder decoder) {
+            mSize = new Size(decoder.mWidth, decoder.mHeight);
+            mDecoder = decoder;
         }
 
         /**
-         * The mimeType of the image, if known.
+         * Size of the image, without scaling or cropping.
          */
+        @NonNull
+        public Size getSize() {
+            return mSize;
+        }
+
+        /**
+         * The mimeType of the image.
+         */
+        @NonNull
         public String getMimeType() {
-            return decoder.getMimeType();
+            return mDecoder.getMimeType();
         }
     };
 
     /**
-     *  Used if the provided data is incomplete.
-     *
-     *  May be thrown if there is nothing to display.
-     *
-     *  If supplied to onPartialImage, there may be a correct partial image to
-     *  display.
+     *  Thrown if the provided data is incomplete.
      */
     public static class IncompleteException extends IOException {};
 
     /**
-     *  Used if the provided data is corrupt.
-     *
-     *  May be thrown if there is nothing to display.
-     *
-     *  If supplied to onPartialImage, there may be a correct partial image to
-     *  display.
-     */
-    public static class CorruptException extends IOException {};
-
-    /**
      *  Optional listener supplied to {@link #decodeDrawable} or
      *  {@link #decodeBitmap}.
      */
@@ -329,31 +354,55 @@
         /**
          *  Called when the header is decoded and the size is known.
          *
-         *  @param info Information about the encoded image.
          *  @param decoder allows changing the default settings of the decode.
+         *  @param info Information about the encoded image.
+         *  @param source that created the decoder.
          */
-        public void onHeaderDecoded(ImageInfo info, ImageDecoder decoder);
+        public void onHeaderDecoded(@NonNull ImageDecoder decoder,
+                @NonNull ImageInfo info, @NonNull Source source);
 
     };
 
     /**
+     *  An Exception was thrown reading the {@link Source}.
+     */
+    public static final int ERROR_SOURCE_EXCEPTION  = 1;
+
+    /**
+     *  The encoded data was incomplete.
+     */
+    public static final int ERROR_SOURCE_INCOMPLETE = 2;
+
+    /**
+     *  The encoded data contained an error.
+     */
+    public static final int ERROR_SOURCE_ERROR      = 3;
+
+    @Retention(SOURCE)
+    @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
+    public @interface Error {};
+
+    /**
      *  Optional listener supplied to the ImageDecoder.
+     *
+     *  Without this listener, errors will throw {@link java.io.IOException}.
      */
     public static interface OnPartialImageListener {
         /**
          *  Called when there is only a partial image to display.
          *
-         *  If the input is incomplete or contains an error, this listener lets
-         *  the client know that and allows them to optionally bypass the rest
-         *  of the decode/creation process.
+         *  If decoding is interrupted after having decoded a partial image,
+         *  this listener lets the client know that and allows them to
+         *  optionally finish the rest of the decode/creation process to create
+         *  a partial {@link Drawable}/{@link Bitmap}.
          *
-         *  @param e IOException containing information about the error that
-         *      interrupted the decode.
-         *  @return True (which is the default) to create and return a
-         *      {@link Drawable}/{@link Bitmap} with partial data. False to
-         *      abort the decode and throw the {@link java.io.IOException}.
+         *  @param error indicating what interrupted the decode.
+         *  @param source that had the error.
+         *  @return True to create and return a {@link Drawable}/{@link Bitmap}
+         *      with partial data. False (which is the default) to abort the
+         *      decode and throw {@link java.io.IOException}.
          */
-        public boolean onPartialImage(IOException e);
+        public boolean onPartialImage(@Error int error, @NonNull Source source);
     };
 
     // Fields
@@ -364,14 +413,15 @@
 
     private int     mDesiredWidth;
     private int     mDesiredHeight;
-    private int     mAllocator = DEFAULT_ALLOCATOR;
+    private int     mAllocator = ALLOCATOR_DEFAULT;
     private boolean mRequireUnpremultiplied = false;
     private boolean mMutable = false;
     private boolean mPreferRamOverQuality = false;
     private boolean mAsAlphaMask = false;
     private Rect    mCropRect;
+    private Source  mSource;
 
-    private PostProcess            mPostProcess;
+    private PostProcessor          mPostProcessor;
     private OnPartialImageListener mOnPartialImageListener;
 
     // Objects for interacting with the input.
@@ -412,6 +462,7 @@
 
     /**
      * Create a new {@link Source} from an asset.
+     * @hide
      *
      * @param res the {@link Resources} object containing the image data.
      * @param resId resource ID of the image data.
@@ -441,6 +492,7 @@
 
     /**
      * Create a new {@link Source} from a byte array.
+     *
      * @param data byte array of compressed image data.
      * @param offset offset into data for where the decoder should begin
      *      parsing.
@@ -448,7 +500,9 @@
      * @throws NullPointerException if data is null.
      * @throws ArrayIndexOutOfBoundsException if offset and length are
      *      not within data.
+     * @hide
      */
+    @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
         if (data == null) {
@@ -464,7 +518,9 @@
 
     /**
      * See {@link #createSource(byte[], int, int).
+     * @hide
      */
+    @NonNull
     public static Source createSource(@NonNull byte[] data) {
         return createSource(data, 0, data.length);
     }
@@ -472,13 +528,15 @@
     /**
      * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
      *
-     * The returned {@link Source} effectively takes ownership of the
+     * <p>The returned {@link Source} effectively takes ownership of the
      * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
-     * this call.
+     * this call.</p>
      *
-     * Decoding will start from {@link java.nio.ByteBuffer#position()}.
+     * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
+     * position after decoding is undefined.
      */
-    public static Source createSource(ByteBuffer buffer) {
+    @NonNull
+    public static Source createSource(@NonNull ByteBuffer buffer) {
         return new ByteBufferSource(buffer);
     }
 
@@ -499,20 +557,29 @@
     }
 
     /**
+     * Create a new {@link Source} from a {@link java.io.File}.
+     */
+    @NonNull
+    public static Source createSource(@NonNull File file) {
+        return new FileSource(file);
+    }
+
+    /**
      *  Return the width and height of a given sample size.
      *
-     *  This takes an input that functions like
+     *  <p>This takes an input that functions like
      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
      *  height that can be acheived by sampling the encoded image. Other widths
      *  and heights may be supported, but will require an additional (internal)
      *  scaling step. Such internal scaling is *not* supported with
-     *  {@link #requireUnpremultiplied}.
+     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
-     *  @return Point {@link Point#x} and {@link Point#y} correspond to the
-     *      width and height after sampling.
+     *  @return {@link android.util.Size} of the width and height after
+     *      sampling.
      */
-    public Point getSampledSize(int sampleSize) {
+    @NonNull
+    public Size getSampledSize(int sampleSize) {
         if (sampleSize <= 0) {
             throw new IllegalArgumentException("sampleSize must be positive! "
                     + "provided " + sampleSize);
@@ -531,7 +598,7 @@
      *  @param width must be greater than 0.
      *  @param height must be greater than 0.
      */
-    public void resize(int width, int height) {
+    public void setResize(int width, int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -544,14 +611,14 @@
     /**
      *  Resize based on a sample size.
      *
-     *  This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #resize(int, int)}.
+     *  <p>This has the same effect as passing the result of
+     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
      */
-    public void resize(int sampleSize) {
-        Point dimensions = this.getSampledSize(sampleSize);
-        this.resize(dimensions.x, dimensions.y);
+    public void setResize(int sampleSize) {
+        Size size = this.getSampledSize(sampleSize);
+        this.setResize(size.getWidth(), size.getHeight());
     }
 
     private boolean requestedResize() {
@@ -567,7 +634,7 @@
      *  switch to software when HARDWARE is incompatible, e.g.
      *  {@link #setMutable}, {@link #setAsAlphaMask}.
      */
-    public static final int DEFAULT_ALLOCATOR = 0;
+    public static final int ALLOCATOR_DEFAULT = 0;
 
     /**
      *  Use a software allocation for the pixel memory.
@@ -575,28 +642,29 @@
      *  Useful for drawing to a software {@link Canvas} or for
      *  accessing the pixels on the final output.
      */
-    public static final int SOFTWARE_ALLOCATOR = 1;
+    public static final int ALLOCATOR_SOFTWARE = 1;
 
     /**
      *  Use shared memory for the pixel memory.
      *
      *  Useful for sharing across processes.
      */
-    public static final int SHARED_MEMORY_ALLOCATOR = 2;
+    public static final int ALLOCATOR_SHARED_MEMORY = 2;
 
     /**
      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
      *
-     *  This will throw an {@link java.lang.IllegalStateException} when combined
-     *  with incompatible options, like {@link #setMutable} or
-     *  {@link #setAsAlphaMask}.
+     *  When this is combined with incompatible options, like
+     *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
+     *  / {@link #decodeBitmap} will throw an
+     *  {@link java.lang.IllegalStateException}.
      */
-    public static final int HARDWARE_ALLOCATOR = 3;
+    public static final int ALLOCATOR_HARDWARE = 3;
 
     /** @hide **/
     @Retention(SOURCE)
-    @IntDef({ DEFAULT_ALLOCATOR, SOFTWARE_ALLOCATOR, SHARED_MEMORY_ALLOCATOR,
-              HARDWARE_ALLOCATOR })
+    @IntDef({ ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, ALLOCATOR_SHARED_MEMORY,
+              ALLOCATOR_HARDWARE })
     public @interface Allocator {};
 
     /**
@@ -604,47 +672,46 @@
      *
      *  This is ignored for animated drawables.
      *
-     *  TODO: Allow accessing the backing from the Bitmap.
-     *
      *  @param allocator Type of allocator to use.
      */
     public void setAllocator(@Allocator int allocator) {
-        if (allocator < DEFAULT_ALLOCATOR || allocator > HARDWARE_ALLOCATOR) {
+        if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
             throw new IllegalArgumentException("invalid allocator " + allocator);
         }
         mAllocator = allocator;
     }
 
     /**
-     *  Create a {@link Bitmap} with unpremultiplied pixels.
+     *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
      *
      *  By default, ImageDecoder will create a {@link Bitmap} with
      *  premultiplied pixels, which is required for drawing with the
      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
-     *  this method will result in {@link #decodeBitmap} returning a
-     *  {@link Bitmap} with unpremultiplied pixels. See
-     *  {@link Bitmap#isPremultiplied}. Incompatible with
+     *  this method with a value of {@code true} will result in
+     *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
+     *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
      *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
      *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
      */
-    public void requireUnpremultiplied() {
-        mRequireUnpremultiplied = true;
+    public void setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+        mRequireUnpremultiplied = requireUnpremultiplied;
     }
 
     /**
      *  Modify the image after decoding and scaling.
      *
-     *  This allows adding effects prior to returning a {@link Drawable} or
+     *  <p>This allows adding effects prior to returning a {@link Drawable} or
      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
-     *  this is the only way to process the image after decoding.
+     *  this is the only way to process the image after decoding.</p>
      *
-     *  If set on a nine-patch image, the nine-patch data is ignored.
+     *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
      *
-     *  For an animated image, the drawing commands drawn on the {@link Canvas}
-     *  will be recorded immediately and then applied to each frame.
+     *  <p>For an animated image, the drawing commands drawn on the
+     *  {@link Canvas} will be recorded immediately and then applied to each
+     *  frame.</p>
      */
-    public void setPostProcess(PostProcess p) {
-        mPostProcess = p;
+    public void setPostProcessor(@Nullable PostProcessor p) {
+        mPostProcessor = p;
     }
 
     /**
@@ -653,66 +720,72 @@
      *  Will be called if there is an error in the input. Without one, a
      *  partial {@link Bitmap} will be created.
      */
-    public void setOnPartialImageListener(OnPartialImageListener l) {
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
         mOnPartialImageListener = l;
     }
 
     /**
      *  Crop the output to {@code subset} of the (possibly) scaled image.
      *
-     *  {@code subset} must be contained within the size set by {@link #resize}
-     *  or the bounds of the image if resize was not called. Otherwise an
-     *  {@link IllegalStateException} will be thrown.
+     *  <p>{@code subset} must be contained within the size set by
+     *  {@link #setResize} or the bounds of the image if setResize was not
+     *  called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
      *
-     *  NOT intended as a replacement for
+     *  <p>NOT intended as a replacement for
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
-     *  but merely crops the output.
+     *  but merely crops the output.</p>
      */
-    public void crop(Rect subset) {
+    public void setCrop(@Nullable Rect subset) {
         mCropRect = subset;
     }
 
     /**
-     *  Create a mutable {@link Bitmap}.
+     *  Specify whether the {@link Bitmap} should be mutable.
      *
-     *  By default, a {@link Bitmap} created will be immutable, but that can be
-     *  changed with this call.
+     *  <p>By default, a {@link Bitmap} created will be immutable, but that can
+     *  be changed with this call.</p>
      *
-     *  Incompatible with {@link #HARDWARE_ALLOCATOR}, because
-     *  {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable. Attempting to
-     *  combine them will throw an {@link java.lang.IllegalStateException}.
+     *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
+     *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
+     *  Attempting to combine them will throw an
+     *  {@link java.lang.IllegalStateException}.</p>
      *
-     *  Incompatible with {@link #decodeDrawable}, which would require
-     *  retrieving the Bitmap from the returned Drawable in order to modify.
-     *  Attempting to decode a mutable {@link Drawable} will throw an
-     *  {@link java.lang.IllegalStateException}
+     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
+     *  which would require retrieving the Bitmap from the returned Drawable in
+     *  order to modify. Attempting to decode a mutable {@link Drawable} will
+     *  throw an {@link java.lang.IllegalStateException}.</p>
      */
-    public void setMutable() {
-        mMutable = true;
+    public void setMutable(boolean mutable) {
+        mMutable = mutable;
     }
 
     /**
-     *  Potentially save RAM at the expense of quality.
+     *  Specify whether to potentially save RAM at the expense of quality.
      *
-     *  This may result in a {@link Bitmap} with a denser {@link Bitmap.Config},
-     *  depending on the image. For example, for an opaque {@link Bitmap}, this
-     *  may result in a {@link Bitmap.Config} with no alpha information.
+     *  Setting this to {@code true} may result in a {@link Bitmap} with a
+     *  denser {@link Bitmap.Config}, depending on the image. For example, for
+     *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
+     *  with no alpha information.
      */
-    public void setPreferRamOverQuality() {
-        mPreferRamOverQuality = true;
+    public void setPreferRamOverQuality(boolean preferRamOverQuality) {
+        mPreferRamOverQuality = preferRamOverQuality;
     }
 
     /**
-     *  Potentially treat the output as an alpha mask.
+     *  Specify whether to potentially treat the output as an alpha mask.
      *
-     *  If the image is encoded in a format with only one channel, treat that
-     *  channel as alpha. Otherwise this call has no effect.
+     *  <p>If this is set to {@code true} and the image is encoded in a format
+     *  with only one channel, treat that channel as alpha. Otherwise this call has
+     *  no effect.</p>
      *
-     *  Incompatible with {@link #HARDWARE_ALLOCATOR}. Trying to combine them
-     *  will throw an {@link java.lang.IllegalStateException}.
+     *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+     *  combine them will result in {@link #decodeDrawable}/
+     *  {@link #decodeBitmap} throwing an
+     *  {@link java.lang.IllegalStateException}.</p>
      */
-    public void setAsAlphaMask() {
-        mAsAlphaMask = true;
+    public void setAsAlphaMask(boolean asAlphaMask) {
+        mAsAlphaMask = asAlphaMask;
     }
 
     @Override
@@ -739,7 +812,7 @@
 
         checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
 
-        if (mAllocator == HARDWARE_ALLOCATOR) {
+        if (mAllocator == ALLOCATOR_HARDWARE) {
             if (mMutable) {
                 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
             }
@@ -748,7 +821,7 @@
             }
         }
 
-        if (mPostProcess != null && mRequireUnpremultiplied) {
+        if (mPostProcessor != null && mRequireUnpremultiplied) {
             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
         }
     }
@@ -763,18 +836,34 @@
         }
     }
 
+    @NonNull
     private Bitmap decodeBitmap() throws IOException {
         checkState();
-        // nDecodeBitmap calls postProcessAndRelease only if mPostProcess
+        // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
+        // exists
+        ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
+        // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
         // exists.
-        ImageDecoder postProcessPtr = mPostProcess == null ? null : this;
-        return nDecodeBitmap(mNativePtr, mOnPartialImageListener,
+        ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
+        return nDecodeBitmap(mNativePtr, partialImagePtr,
                 postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
                 mMutable, mAllocator, mRequireUnpremultiplied,
                 mPreferRamOverQuality, mAsAlphaMask);
 
     }
 
+    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
+            @NonNull Source src) {
+        if (listener != null) {
+            ImageInfo info = new ImageInfo(this);
+            try {
+                listener.onHeaderDecoded(this, info, src);
+            } finally {
+                info.mDecoder = null;
+            }
+        }
+    }
+
     /**
      *  Create a {@link Drawable} from a {@code Source}.
      *
@@ -791,14 +880,8 @@
     public static Drawable decodeDrawable(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
         try (ImageDecoder decoder = src.createImageDecoder()) {
-            if (listener != null) {
-                ImageInfo info = new ImageInfo(decoder);
-                try {
-                    listener.onHeaderDecoded(info, decoder);
-                } finally {
-                    info.decoder = null;
-                }
-            }
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
 
             if (decoder.mRequireUnpremultiplied) {
                 // Though this could be supported (ignored) for opaque images,
@@ -817,8 +900,8 @@
             final int srcDensity = computeDensity(src, decoder);
             if (decoder.mAnimated) {
                 // AnimatedImageDrawable calls postProcessAndRelease only if
-                // mPostProcess exists.
-                ImageDecoder postProcessPtr = decoder.mPostProcess == null ?
+                // mPostProcessor exists.
+                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
                         null : decoder;
                 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
                         postProcessPtr, decoder.mDesiredWidth,
@@ -878,14 +961,8 @@
     public static Bitmap decodeBitmap(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
         try (ImageDecoder decoder = src.createImageDecoder()) {
-            if (listener != null) {
-                ImageInfo info = new ImageInfo(decoder);
-                try {
-                    listener.onHeaderDecoded(info, decoder);
-                } finally {
-                    info.decoder = null;
-                }
-            }
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
 
             // this call potentially manipulates the decoder so it must be performed prior to
             // decoding the bitmap
@@ -922,13 +999,14 @@
             float scale = (float) dstDensity / srcDensity;
             int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
             int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-            decoder.resize(scaledWidth, scaledHeight);
+            decoder.setResize(scaledWidth, scaledHeight);
             return dstDensity;
         }
 
         return srcDensity;
     }
 
+    @NonNull
     private String getMimeType() {
         return nGetMimeType(mNativePtr);
     }
@@ -945,14 +1023,22 @@
      * Private method called by JNI.
      */
     @SuppressWarnings("unused")
-    private int postProcessAndRelease(@NonNull Canvas canvas, int width, int height) {
+    private int postProcessAndRelease(@NonNull Canvas canvas) {
         try {
-            return mPostProcess.postProcess(canvas, width, height);
+            return mPostProcessor.onPostProcess(canvas);
         } finally {
             canvas.release();
         }
     }
 
+    /**
+     * Private method called by JNI.
+     */
+    @SuppressWarnings("unused")
+    private boolean onPartialImage(@Error int error) {
+        return mOnPartialImageListener.onPartialImage(error, mSource);
+    }
+
     private static native ImageDecoder nCreate(long asset) throws IOException;
     private static native ImageDecoder nCreate(ByteBuffer buffer,
                                                int position,
@@ -960,19 +1046,20 @@
     private static native ImageDecoder nCreate(byte[] data, int offset,
                                                int length) throws IOException;
     private static native ImageDecoder nCreate(InputStream is, byte[] storage);
+    // The fd must be seekable.
     private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
     @NonNull
     private static native Bitmap nDecodeBitmap(long nativePtr,
-            OnPartialImageListener listener,
-            @Nullable ImageDecoder decoder,     // Only used if mPostProcess != null
+            @Nullable ImageDecoder partialImageListener,
+            @Nullable ImageDecoder postProcessor,
             int width, int height,
-            Rect cropRect, boolean mutable,
+            @Nullable Rect cropRect, boolean mutable,
             int allocator, boolean requireUnpremul,
             boolean preferRamOverQuality, boolean asAlphaMask)
         throws IOException;
-    private static native Point nGetSampledSize(long nativePtr,
-                                                int sampleSize);
-    private static native void nGetPadding(long nativePtr, Rect outRect);
+    private static native Size nGetSampledSize(long nativePtr,
+                                               int sampleSize);
+    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
     private static native void nClose(long nativePtr);
     private static native String nGetMimeType(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/PostProcess.java b/graphics/java/android/graphics/PostProcessor.java
similarity index 78%
rename from graphics/java/android/graphics/PostProcess.java
rename to graphics/java/android/graphics/PostProcessor.java
index c5a31e8..b1712e9 100644
--- a/graphics/java/android/graphics/PostProcess.java
+++ b/graphics/java/android/graphics/PostProcessor.java
@@ -20,38 +20,38 @@
 import android.annotation.NonNull;
 import android.graphics.drawable.Drawable;
 
-
 /**
  *  Helper interface for adding custom processing to an image.
  *
- *  The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
+ *  <p>The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
  *  of an animated image produced by {@link ImageDecoder}. This is called before
- *  the requested object is returned.
+ *  the requested object is returned.</p>
  *
- *  This custom processing also applies to image types that are otherwise
- *  immutable, such as {@link Bitmap.Config#HARDWARE}.
+ *  <p>This custom processing also applies to image types that are otherwise
+ *  immutable, such as {@link Bitmap.Config#HARDWARE}.</p>
  *
- *  On an animated image, the callback will only be called once, but the drawing
+ *  <p>On an animated image, the callback will only be called once, but the drawing
  *  commands will be applied to each frame, as if the {@code Canvas} had been
- *  returned by {@link Picture#beginRecording}.
+ *  returned by {@link Picture#beginRecording}.<p>
  *
- *  Supplied to ImageDecoder via {@link ImageDecoder#setPostProcess}.
- *  @hide
+ *  <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor}.</p>
  */
-public interface PostProcess {
+public interface PostProcessor {
     /**
      *  Do any processing after (for example) decoding.
      *
-     *  Drawing to the {@link Canvas} will behave as if the initial processing
+     *  <p>Drawing to the {@link Canvas} will behave as if the initial processing
      *  (e.g. decoding) already exists in the Canvas. An implementation can draw
      *  effects on top of this, or it can even draw behind it using
      *  {@link PorterDuff.Mode#DST_OVER}. A common effect is to add transparency
      *  to the corners to achieve rounded corners. That can be done with the
-     *  following code:
+     *  following code:</p>
      *
      *  <code>
      *      Path path = new Path();
      *      path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+     *      int width = canvas.getWidth();
+     *      int height = canvas.getHeight();
      *      path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
      *      Paint paint = new Paint();
      *      paint.setAntiAlias(true);
@@ -63,10 +63,6 @@
      *
      *
      *  @param canvas The {@link Canvas} to draw to.
-     *  @param width Width of {@code canvas}. Anything drawn outside of this
-     *      will be ignored.
-     *  @param height Height of {@code canvas}. Anything drawn outside of this
-     *      will be ignored.
      *  @return Opacity of the result after drawing.
      *      {@link PixelFormat#UNKNOWN} means that the implementation did not
      *      change whether the image has alpha. Return this unless you added
@@ -87,5 +83,5 @@
      *      {@link java.lang.IllegalArgumentException}.
      */
     @PixelFormat.Opacity
-    public int postProcess(@NonNull Canvas canvas, int width, int height);
+    public int onPostProcess(@NonNull Canvas canvas);
 }
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f74c39d..3a5f7b7 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -165,8 +165,8 @@
         Bitmap bitmap = null;
         try (FileInputStream stream = new FileInputStream(filepath)) {
             bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
-                    (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (Exception e) {
             /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
@@ -198,8 +198,8 @@
         Bitmap bitmap = null;
         try {
             bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
-                    (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (Exception e) {
             /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
@@ -838,8 +838,8 @@
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
                 ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
-                bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> {
-                    decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
                 });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 291b0a0..36a4d26 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1229,8 +1229,8 @@
                 source = ImageDecoder.createSource(res, is);
             }
 
-            return ImageDecoder.decodeDrawable(source, (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (IOException e) {
             /*  do nothing.
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index e2f02df..77925fd 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,6 +142,7 @@
 
 static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
                             SkPaint* paint) {
+    paint->setFilterQuality(kLow_SkFilterQuality);
     if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
         properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
         paint->setAlpha(properties.alpha() * alphaMultiplier);
@@ -200,18 +201,15 @@
         // composing a hardware layer
         if (renderNode->getLayerSurface() && mComposeLayer) {
             SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
-            SkPaint* paint = nullptr;
-            SkPaint tmpPaint;
-            if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
-                paint = &tmpPaint;
-            }
+            SkPaint paint;
+            layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
 
             // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
             // we need to restrict the portion of the surface drawn to the size of the renderNode.
             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
             canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
-                                  bounds, bounds, paint);
+                    bounds, bounds, &paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index c7f57fe..2953ea8 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1046,6 +1046,40 @@
     EXPECT_EQ(2, canvas.mDrawCounter);
 }
 
+// Verify that layers are composed with kLow_SkFilterQuality filter quality.
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+    static const int CANVAS_WIDTH = 1;
+    static const int CANVAS_HEIGHT = 1;
+    static const int LAYER_WIDTH = 1;
+    static const int LAYER_HEIGHT = 1;
+    class FrameTestCanvas : public TestCanvasBase {
+    public:
+        FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                const SkPaint* paint, SrcRectConstraint constraint) override {
+            mDrawCounter++;
+            EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
+        }
+    };
+
+    auto layerNode = TestUtils::createSkiaNode(
+            0, 0, LAYER_WIDTH, LAYER_HEIGHT,
+            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+                canvas.drawPaint(SkPaint());
+            });
+
+    layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+    layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
+
+    FrameTestCanvas canvas;
+    RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
+    canvas.drawDrawable(&drawable);
+    EXPECT_EQ(1, canvas.mDrawCounter);  //make sure the layer was composed
+
+    // clean up layer pointer, so we can safely destruct RenderNode
+    layerNode->setLayerSurface(nullptr);
+}
+
 TEST(ReorderBarrierDrawable, testShadowMatrix) {
     static const int CANVAS_WIDTH = 100;
     static const int CANVAS_HEIGHT = 100;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 46fe89a..b07d042 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -238,25 +238,13 @@
     public static final int ENCODING_DTS = 7;
     /** Audio data format: DTS HD compressed */
     public static final int ENCODING_DTS_HD = 8;
-    /** Audio data format: MP3 compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: MP3 compressed */
     public static final int ENCODING_MP3 = 9;
-    /** Audio data format: AAC LC compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: AAC LC compressed */
     public static final int ENCODING_AAC_LC = 10;
-    /** Audio data format: AAC HE V1 compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: AAC HE V1 compressed */
     public static final int ENCODING_AAC_HE_V1 = 11;
-    /** Audio data format: AAC HE V2 compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: AAC HE V2 compressed */
     public static final int ENCODING_AAC_HE_V2 = 12;
 
     /** Audio data format: compressed audio wrapped in PCM for HDMI
@@ -271,16 +259,12 @@
     /** Audio data format: DOLBY TRUEHD compressed
      **/
     public static final int ENCODING_DOLBY_TRUEHD = 14;
-    /** Audio data format: AAC ELD compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: AAC ELD compressed */
     public static final int ENCODING_AAC_ELD = 15;
-    /** Audio data format: AAC xHE compressed
-     * @hide
-     * TODO unhide and add to @Encoding (intentional white space   
-     * */
+    /** Audio data format: AAC xHE compressed */
     public static final int ENCODING_AAC_XHE = 16;
+    /** Audio data format: AC-4 sync frame transport format */
+    public static final int ENCODING_AC4 = 17;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -317,6 +301,8 @@
                 return "ENCODING_AAC_ELD";
             case ENCODING_AAC_XHE:
                 return "ENCODING_AAC_XHE";
+            case ENCODING_AC4:
+                return "ENCODING_AC4";
             default :
                 return "invalid encoding " + enc;
         }
@@ -535,6 +521,7 @@
         case ENCODING_IEC61937:
         case ENCODING_AAC_ELD:
         case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return true;
         default:
             return false;
@@ -553,13 +540,13 @@
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_IEC61937:
-            //TODO not true yet (intended white space     
         case ENCODING_MP3:
         case ENCODING_AAC_LC:
         case ENCODING_AAC_HE_V1:
         case ENCODING_AAC_HE_V2:
         case ENCODING_AAC_ELD:
         case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return true;
         default:
             return false;
@@ -586,6 +573,7 @@
         case ENCODING_IEC61937: // wrapped in PCM but compressed
         case ENCODING_AAC_ELD:
         case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return false;
         case ENCODING_INVALID:
         default:
@@ -613,6 +601,7 @@
         case ENCODING_AAC_HE_V2:
         case ENCODING_AAC_ELD:
         case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return false;
         case ENCODING_INVALID:
         default:
@@ -849,6 +838,7 @@
                 case ENCODING_AAC_HE_V2:
                 case ENCODING_AAC_ELD:
                 case ENCODING_AAC_XHE:
+                case ENCODING_AC4:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1056,7 +1046,13 @@
         ENCODING_E_AC3,
         ENCODING_DTS,
         ENCODING_DTS_HD,
-        ENCODING_IEC61937 }
+        ENCODING_IEC61937,
+        ENCODING_AAC_HE_V1,
+        ENCODING_AAC_HE_V2,
+        ENCODING_AAC_LC,
+        ENCODING_AAC_ELD,
+        ENCODING_AAC_XHE,
+        ENCODING_AC4 }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4d5343c..2ac4063 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1331,15 +1331,13 @@
     //====================================================================
     // Offload query
     /**
-     * @hide
-     * TODO unhide (intentional white space to attract attention:    
      * Returns whether offloaded playback of an audio format is supported on the device.
      * Offloaded playback is where the decoding of an audio stream is not competing with other
      * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
      * @param format the audio format (codec, sample rate, channels) being checked.
      * @return true if the given audio format can be offloaded.
      */
-    public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+    public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
         return AudioSystem.isOffloadSupported(format);
     }
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6add381..5928d03 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -755,8 +755,8 @@
      * <code>MODE_STREAM</code> will be used.
      * <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
      * be generated.
+     * <br>Offload is false by default.
      */
-    // TODO add that offload is false by default (intended white space   
     public static class Builder {
         private AudioAttributes mAttributes;
         private AudioFormat mFormat;
@@ -895,14 +895,11 @@
         }
 
         /**
-         * @hide
-         * TODO unhide (intentional whitespace    
-         * TODO should offload require POWER_SAVING?    
          * Sets whether this track will play through the offloaded audio path.
          * When set to true, at build time, the audio format will be checked against
          * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
          * used by this track is supported on the device's offload path (if any).
-         * <br>Offload is only supported for media audio data, and therefore require that
+         * <br>Offload is only supported for media audio streams, and therefore requires that
          * the usage be {@link AudioAttributes#USAGE_MEDIA}.
          * @param offload true to require the offload path for playback.
          * @return the same Builder instance.
@@ -962,7 +959,7 @@
                     throw new UnsupportedOperationException(
                             "Cannot create AudioTrack, offload requires USAGE_MEDIA");
                 }
-                if (!AudioManager.isOffloadedPlaybackSupported(mFormat)) {
+                if (!AudioSystem.isOffloadSupported(mFormat)) {
                     throw new UnsupportedOperationException(
                             "Cannot create AudioTrack, offload format not supported");
                 }
@@ -2942,14 +2939,31 @@
     }
 
     /**
-     * @hide
-     * TODO unhide (intentional white space to attract attention:    
      * Abstract class to receive event notification about the stream playback.
+     * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+     * the callback on the given {@link AudioTrack} instance.
      */
     public abstract static class StreamEventCallback {
-        // TODO rename if supported for non offload tracks
+        /** @hide */ // add hidden empty constructor so it doesn't show in SDK
+        public StreamEventCallback() { }
+        /**
+         * Called when an offloaded track is no longer valid and has been discarded by the system.
+         * An example of this happening is when an offloaded track has been paused too long, and
+         * gets invalidated by the system to prevent any other offload.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
         public void onTearDown(AudioTrack track) { }
+        /**
+         * Called when all the buffers of an offloaded track that were queued in the audio system
+         * (e.g. the combination of the Android audio framework and the device's audio hardware)
+         * have been played after {@link AudioTrack#stop()} has been called.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
         public void onStreamPresentationEnd(AudioTrack track) { }
+        /**
+         * Called when more audio data can be written without blocking on an offloaded track.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
         public void onStreamDataRequest(AudioTrack track) { }
     }
 
@@ -2958,11 +2972,9 @@
     private final Object mStreamEventCbLock = new Object();
 
     /**
-     * @hide
-     * TODO unhide (intentional white space to attract attention:    
-     * Registers a callback for notification of stream events.
+     * Sets the callback for the notification of stream events.
      * @param executor {@link Executor} to handle the callbacks
-     * @param eventCallback the callback to receive the stream events
+     * @param eventCallback the callback to receive the stream event notifications
      */
     public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull StreamEventCallback eventCallback) {
@@ -2979,9 +2991,8 @@
     }
 
     /**
-     * @hide
      * Unregisters the callback for notification of stream events, previously set
-     * by {@link #setStreamEventCallback(StreamEventCallback, Executor)}.
+     * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
      */
     public void removeStreamEventCallback() {
         synchronized (mStreamEventCbLock) {
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
new file mode 100644
index 0000000..bc8121e
--- /dev/null
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -0,0 +1,65 @@
+/*
+ * 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.media.session.PlaybackState;
+import android.media.IMediaSession2Callback;
+import android.os.Bundle;
+
+/**
+ * Interface to MediaSession2. Framework MUST only call oneway APIs.
+ *
+ * @hide
+ */
+// TODO(jaewan): Make this oneway interface.
+//               Malicious app can fake session binder and holds commands from controller.
+interface IMediaSession2 {
+    // TODO(jaewan): add onCommand() to send private command
+    // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order
+    //               Add id for individual calls to address this.
+
+    // TODO(jaewan): We may consider to add another binder just for the connection
+    //               not to expose other methods to the controller whose connection wasn't accepted.
+    //               But this would be enough for now because it's the same as existing
+    //               MediaBrowser and MediaBrowserService.
+    oneway void connect(String callingPackage, IMediaSession2Callback callback);
+    oneway void release(IMediaSession2Callback caller);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // send command
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    oneway void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args);
+
+    PlaybackState getPlaybackState();
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Callbacks -- remove them
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * @param callbackBinder binder to be used to notify changes.
+     * @param callbackFlag one of {@link MediaController2#FLAG_CALLBACK_PLAYBACK} or
+     *     {@link MediaController2#FLAG_CALLBACK_SESSION_ACTIVENESS}
+     * @param requestCode If >= 0, this code will be called back by the callback after the callback
+     *     is registered.
+     */
+    // TODO(jaewan): Due to the nature of the binder, calls can be called out of order.
+    //               Need a way to ensure calling of unregisterCallback unregisters later
+    //               registerCallback.
+    oneway void registerCallback(IMediaSession2Callback callbackBinder,
+            int callbackFlag, int requestCode);
+    oneway void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag);
+}
diff --git a/media/java/android/media/IMediaSession2Callback.aidl b/media/java/android/media/IMediaSession2Callback.aidl
new file mode 100644
index 0000000..0058e79
--- /dev/null
+++ b/media/java/android/media/IMediaSession2Callback.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.os.Bundle;
+import android.media.session.PlaybackState;
+import android.media.IMediaSession2;
+
+/**
+ * Interface from MediaSession2 to MediaSession2Record.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ *
+ * @hide
+ */
+oneway interface IMediaSession2Callback {
+    void onPlaybackStateChanged(in PlaybackState state);
+
+    /**
+     * Called only when the controller is created with service's token.
+     *
+     * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session
+     *     binder if the connect is accepted.
+     * @param commands initially allowed commands.
+     */
+    // TODO(jaewan): Also need to pass flags for allowed actions for permission check.
+    //               For example, a media can allow setRating only for whitelisted apps
+    //               it's better for controller to know such information in advance.
+    //               Follow-up TODO: Add similar functions to the session.
+    // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
+    void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
new file mode 100644
index 0000000..550eee2
--- /dev/null
+++ b/media/java/android/media/MediaController2.java
@@ -0,0 +1,197 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaController2Provider;
+import android.os.Handler;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows an app to interact with an active {@link MediaSession2} or a
+ * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
+ * the session.
+ * <p>
+ * When you're done, use {@link #release()} to clean up resources. This also helps session service
+ * to be destroyed when there's no controller associated with it.
+ * <p>
+ * When controlling {@link MediaSession2}, the controller will be available immediately after
+ * the creation.
+ * <p>
+ * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
+ * available only if the session service allows this controller by
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait
+ * {@link ControllerCallback#onConnected(CommandGroup)} or
+ * {@link ControllerCallback#onDisconnected()} for the result.
+ * <p>
+ * A controller can be created through token from {@link MediaSessionManager} if you hold the
+ * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
+ * an enabled notification listener or by getting a {@link SessionToken} directly the
+ * the session owner.
+ * <p>
+ * MediaController2 objects are thread-safe.
+ * <p>
+ * @see MediaSession2
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing.
+public class MediaController2 extends MediaPlayerBase {
+    /**
+     * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
+     * active if and only if it has set a player.
+     */
+    public abstract static class ControllerCallback {
+        /**
+         * Called when the controller is successfully connected to the session. The controller
+         * becomes available afterwards.
+         *
+         * @param allowedCommands commands that's allowed by the session.
+         */
+        public void onConnected(CommandGroup allowedCommands) { }
+
+        /**
+         * Called when the session refuses the controller or the controller is disconnected from
+         * the session. The controller becomes unavailable afterwards and the callback wouldn't
+         * be called.
+         * <p>
+         * It will be also called after the {@link #release()}, so you can put clean up code here.
+         * You don't need to call {@link #release()} after this.
+         */
+        public void onDisconnected() { }
+    }
+
+    private final MediaController2Provider mProvider;
+
+    /**
+     * Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session
+     * and may wake up the service if it's not available.
+     *
+     * @param context Context
+     * @param token token to connect to
+     * @param callback controller callback to receive changes in
+     * @param executor executor to run callbacks on.
+     */
+    // TODO(jaewan): Put @CallbackExecutor to the constructor.
+    public MediaController2(@NonNull Context context, @NonNull SessionToken token,
+            @NonNull ControllerCallback callback, @NonNull Executor executor) {
+        super();
+
+        // This also connects to the token.
+        // Explicit connect() isn't added on purpose because retrying connect() is impossible with
+        // session whose session binder is only valid while it's active.
+        // prevent a controller from reusable after the
+        // session is released and recreated.
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaController2(this, context, token, callback, executor);
+    }
+
+    /**
+     * Release this object, and disconnect from the session. After this, callbacks wouldn't be
+     * received.
+     */
+    public void release() {
+        mProvider.release_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public MediaController2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return token
+     */
+    public @NonNull
+    SessionToken getSessionToken() {
+        return mProvider.getSessionToken_impl();
+    }
+
+    /**
+     * Returns whether this class is connected to active {@link MediaSession2} or not.
+     */
+    public boolean isConnected() {
+        return mProvider.isConnected_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @Nullable PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * {@link MediaSession2}.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException Called when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state
+    //               through the listener.
+    // TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(@NonNull PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
new file mode 100644
index 0000000..980c70f
--- /dev/null
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -0,0 +1,69 @@
+/*
+ * 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.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * Tentative interface for all media players that want media session.
+ * APIs are named to avoid conflicts with MediaPlayer APIs.
+ * All calls should be asynchrounous.
+ *
+ * @hide
+ */
+// TODO(wjia) Finalize the list of MediaPlayer2, which MediaPlayerBase's APIs will be come from.
+public abstract class MediaPlayerBase {
+    /**
+     * Listens change in {@link PlaybackState}.
+     */
+    public interface PlaybackListener {
+        /**
+         * Called when {@link PlaybackState} for this player is changed.
+         */
+        void onPlaybackChanged(PlaybackState state);
+    }
+
+    // TODO(jaewan): setDataSources()?
+    // TODO(jaewan): Add release() or do that in stop()?
+
+    // TODO(jaewan): Add set/getSupportedActions().
+    public abstract void play();
+    public abstract void pause();
+    public abstract void stop();
+    public abstract void skipToPrevious();
+    public abstract void skipToNext();
+
+    // Currently PlaybackState's error message is the content title (for testing only)
+    // TODO(jaewan): Add metadata support
+    public abstract PlaybackState getPlaybackState();
+
+    /**
+     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     */
+    public abstract void addPlaybackListener(PlaybackListener listener, Handler handler);
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     */
+    public abstract void removePlaybackListener(PlaybackListener listener);
+}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 3c49b80..78477f7 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1380,7 +1380,8 @@
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
                 enableNativeRoutingCallbacksLocked(true);
                 mRoutingChangeListeners.put(
-                        listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                                handler != null ? handler : mEventHandler));
             }
         }
     }
@@ -1401,36 +1402,6 @@
         }
     }
 
-    /**
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeRoutingEventHandlerDelegate {
-        private MediaRecorder mMediaRecorder;
-        private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
-        private Handler mHandler;
-
-        NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder,
-                final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
-            mMediaRecorder = mediaRecorder;
-            mOnRoutingChangedListener = listener;
-            mHandler = handler != null ? handler : mEventHandler;
-        }
-
-        void notifyClient() {
-            if (mHandler != null) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mOnRoutingChangedListener != null) {
-                            mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder);
-                        }
-                    }
-                });
-            }
-        }
-    }
-
     private native final boolean native_setInputDevice(int deviceId);
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
new file mode 100644
index 0000000..807535b
--- /dev/null
+++ b/media/java/android/media/MediaSession2.java
@@ -0,0 +1,607 @@
+/*
+ * 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.session.MediaSession;
+import android.media.session.MediaSession.Callback;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps. Common use cases are as follows.
+ * <ul>
+ *     <li>Bluetooth/wired headset key events support</li>
+ *     <li>Android Auto/Wearable support</li>
+ *     <li>Separating UI process and playback process</li>
+ * </ul>
+ * <p>
+ * A MediaSession2 should be created when an app wants to publish media playback information or
+ * handle media keys. In general an app only needs one session for all playback, though multiple
+ * 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
+ * {@link MediaSessionService2} for details.
+ * <p>
+ * A session can be obtained by {@link #getInstance(Context, Handler)}. The owner of the session may
+ * pass its session token to other processes to allow them to create a {@link MediaController2}
+ * to interact with the session.
+ * <p>
+ * To receive transport control commands, an underlying media player must be set with
+ * {@link #setPlayer(MediaPlayerBase)}. Commands will be sent to the underlying player directly
+ * on the thread that had been specified by {@link #getInstance(Context, Handler)}.
+ * <p>
+ * When an app is finished performing playback it must call
+ * {@link #setPlayer(MediaPlayerBase)} with {@code null} to clean up the session and notify any
+ * controllers. It's developers responsibility of cleaning the session and releasing resources.
+ * <p>
+ * MediaSession2 objects should be used on the handler's thread that is initially given by
+ * {@link #getInstance(Context, Handler)}.
+ *
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently it's borrowed from the MediaSession.
+// TODO(jaewan): Add explicit release(), and make token @NonNull. Session will be active while the
+//               session exists, and controllers will be invalidated when session becomes inactive.
+// TODO(jaewan): Should we support thread safe? It may cause tricky issue such as b/63797089
+// TODO(jaewan): Should we make APIs for MediaSessionService2 public? It's helpful for
+//               developers that doesn't want to override from Browser, but user may not use this
+//               correctly.
+public final class MediaSession2 extends MediaPlayerBase {
+    private final MediaSession2Provider mProvider;
+
+    // Note: Do not define IntDef because subclass can add more command code on top of these.
+    public static final int COMMAND_CODE_CUSTOM = 0;
+    public static final int COMMAND_CODE_PLAYBACK_START = 1;
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+     * <p>
+     * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+     * {@link #getCustomCommand()} shouldn't be {@code null}.
+     */
+    // TODO(jaewan): Move this into the updatable.
+    public static final class Command {
+        private static final String KEY_COMMAND_CODE
+                = "android.media.mediasession2.command.command_command";
+        private static final String KEY_COMMAND_CUSTOM_COMMAND
+                = "android.media.mediasession2.command.custom_command";
+        private static final String KEY_COMMAND_EXTRA
+                = "android.media.mediasession2.command.extra";
+
+        private final int mCommandCode;
+        // Nonnull if it's custom command
+        private final String mCustomCommand;
+        private final Bundle mExtra;
+
+        public Command(int commandCode) {
+            mCommandCode = commandCode;
+            mCustomCommand = null;
+            mExtra = null;
+        }
+
+        public Command(@NonNull String action, @Nullable Bundle extra) {
+            if (action == null) {
+                throw new IllegalArgumentException("action shouldn't be null");
+            }
+            mCommandCode = COMMAND_CODE_CUSTOM;
+            mCustomCommand = action;
+            mExtra = extra;
+        }
+
+        public int getCommandCode() {
+            return mCommandCode;
+        }
+
+        public @Nullable String getCustomCommand() {
+            return mCustomCommand;
+        }
+
+        public @Nullable Bundle getExtra() {
+            return mExtra;
+        }
+
+        /**
+         * @return a new Bundle instance from the Command
+         * @hide
+         */
+        public Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
+            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
+            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
+            return bundle;
+        }
+
+        /**
+         * @return a new Command instance from the Bundle
+         * @hide
+         */
+        public static Command fromBundle(Bundle command) {
+            int code = command.getInt(KEY_COMMAND_CODE);
+            if (code != COMMAND_CODE_CUSTOM) {
+                return new Command(code);
+            } else {
+                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
+                if (customCommand == null) {
+                    return null;
+                }
+                return new Command(customCommand, command.getBundle(KEY_COMMAND_EXTRA));
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Command)) {
+                return false;
+            }
+            Command other = (Command) obj;
+            // TODO(jaewan): Should we also compare contents in bundle?
+            //               It may not be possible if the bundle contains private class.
+            return mCommandCode == other.mCommandCode
+                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            return ((mCustomCommand != null) ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
+        }
+    }
+
+    /**
+     * Represent set of {@link Command}.
+     */
+    // TODO(jaewan): Move this to updatable
+    public static class CommandGroup {
+        private static final String KEY_COMMANDS =
+                "android.media.mediasession2.commandgroup.commands";
+        private ArraySet<Command> mCommands = new ArraySet<>();
+
+        public CommandGroup() {
+        }
+
+        public CommandGroup(CommandGroup others) {
+            mCommands.addAll(others.mCommands);
+        }
+
+        public void addCommand(Command command) {
+            mCommands.add(command);
+        }
+
+        public void addAllPredefinedCommands() {
+            // TODO(jaewan): Is there any better way than this?
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_START));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PAUSE));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        }
+
+        public void removeCommand(Command command) {
+            mCommands.remove(command);
+        }
+
+        public boolean hasCommand(Command command) {
+            return mCommands.contains(command);
+        }
+
+        public boolean hasCommand(int code) {
+            if (code == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+            }
+            for (int i = 0; i < mCommands.size(); i++) {
+                if (mCommands.valueAt(i).getCommandCode() == code) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return new bundle from the CommandGroup
+         * @hide
+         */
+        public Bundle toBundle() {
+            ArrayList<Bundle> list = new ArrayList<>();
+            for (int i = 0; i < mCommands.size(); i++) {
+                list.add(mCommands.valueAt(i).toBundle());
+            }
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(KEY_COMMANDS, list);
+            return bundle;
+        }
+
+        /**
+         * @return new instance of CommandGroup from the bundle
+         * @hide
+         */
+        public static @Nullable CommandGroup fromBundle(Bundle commands) {
+            if (commands == null) {
+                return null;
+            }
+            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
+            if (list == null) {
+                return null;
+            }
+            CommandGroup commandGroup = new CommandGroup();
+            for (int i = 0; i < list.size(); i++) {
+                Parcelable parcelable = list.get(i);
+                if (!(parcelable instanceof Bundle)) {
+                    continue;
+                }
+                Bundle commandBundle = (Bundle) parcelable;
+                Command command = Command.fromBundle(commandBundle);
+                if (command != null) {
+                    commandGroup.addCommand(command);
+                }
+            }
+            return commandGroup;
+        }
+    }
+
+    /**
+     * Callback to be called for all incoming commands from {@link MediaController2}s.
+     * <p>
+     * If it's not set, the session will accept all controllers and all incoming commands by
+     * default.
+     */
+    // TODO(jaewan): Can we move this inside of the updatable for default implementation.
+    public static class SessionCallback {
+        /**
+         * Called when a controller is created for this session. Return allowed commands for
+         * controller. By default it allows all connection requests and commands.
+         * <p>
+         * You can reject the connection by return {@code null}. In that case, controller receives
+         * {@link MediaController2.ControllerCallback#onDisconnected()} and cannot be usable.
+         *
+         * @param controller controller information.
+         * @return allowed commands. Can be {@code null} to reject coonnection.
+         */
+        // TODO(jaewan): Change return type. Once we do, null is for reject.
+        public @Nullable CommandGroup onConnect(@NonNull ControllerInfo controller) {
+            CommandGroup commands = new CommandGroup();
+            commands.addAllPredefinedCommands();
+            return commands;
+        }
+
+        /**
+         * Called when a controller sent a command to the session. You can also reject the request
+         * by return {@code false}.
+         * <p>
+         * This method will be called on the session handler.
+         *
+         * @param controller controller information.
+         * @param command a command. This method will be called for every single command.
+         * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
+         */
+        public boolean onCommandRequest(@NonNull ControllerInfo controller,
+                @NonNull Command command) {
+            return true;
+        }
+    };
+
+    /**
+     * Builder for {@link MediaSession2}.
+     * <p>
+     * Any incoming event from the {@link MediaController2} will be handled on the thread
+     * that created session with the {@link Builder#build()}.
+     */
+    // TODO(jaewan): Move this to updatable
+    // TODO(jaewan): Add setRatingType()
+    // TODO(jaewan): Add setSessionActivity()
+    public final static class Builder {
+        private final Context mContext;
+        private final MediaPlayerBase mPlayer;
+        private String mId;
+        private SessionCallback mCallback;
+
+        /**
+         * Constructor.
+         *
+         * @param context a context
+         * @param player a player to handle incoming command from any controller.
+         * @throws IllegalArgumentException if any parameter is null, or the player is a
+         *      {@link MediaSession2} or {@link MediaController2}.
+         */
+        public Builder(@NonNull Context context, @NonNull MediaPlayerBase player) {
+            if (context == null) {
+                throw new IllegalArgumentException("context shouldn't be null");
+            }
+            if (player == null) {
+                throw new IllegalArgumentException("player shouldn't be null");
+            }
+            if (player instanceof MediaSession2 || player instanceof MediaController2) {
+                throw new IllegalArgumentException("player doesn't accept MediaSession2 nor"
+                        + " MediaController2");
+            }
+            mContext = context;
+            mPlayer = player;
+            // Ensure non-null
+            mId = "";
+        }
+
+        /**
+         * Set ID of the session. If it's not set, an empty string with used to create a session.
+         * <p>
+         * Use this if and only if your app supports multiple playback at the same time and also
+         * wants to provide external apps to have finer controls of them.
+         *
+         * @param id id of the session. Must be unique per package.
+         * @throws IllegalArgumentException if id is {@code null}
+         * @return
+         */
+        public Builder setId(@NonNull String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id shouldn't be null");
+            }
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set {@link SessionCallback}.
+         *
+         * @param callback session callback.
+         * @return
+         */
+        public Builder setSessionCallback(@Nullable SessionCallback callback) {
+            mCallback = callback;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaSession2}.
+         *
+         * @return a new session
+         * @throws IllegalStateException if the session with the same id is already exists for the
+         *      package.
+         */
+        public MediaSession2 build() throws IllegalStateException {
+            if (mCallback == null) {
+                mCallback = new SessionCallback();
+            }
+            return new MediaSession2(mContext, mPlayer, mId, mCallback);
+        }
+    }
+
+    /**
+     * Information of a controller.
+     */
+    // TODO(jaewan): Move implementation to the updatable.
+    public static final class ControllerInfo {
+        private final ControllerInfoProvider mProvider;
+
+        /**
+         * @hide
+         */
+        // TODO(jaewan): SystemApi
+        // TODO(jaewan): Also accept componentName to check notificaiton listener.
+        public ControllerInfo(Context context, int uid, int pid, String packageName,
+                IMediaSession2Callback callback) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2ControllerInfoProvider(
+                            this, context, uid, pid, packageName, callback);
+        }
+
+        /**
+         * @return package name of the controller
+         */
+        public String getPackageName() {
+            return mProvider.getPackageName_impl();
+        }
+
+        /**
+         * @return uid of the controller
+         */
+        public int getUid() {
+            return mProvider.getUid_impl();
+        }
+
+        /**
+         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
+         * has a enabled notification listener so can be trusted to accept connection and incoming
+         * command request.
+         *
+         * @return {@code true} if the controller is trusted.
+         */
+        public boolean isTrusted() {
+            return mProvider.isTrusted_impl();
+        }
+
+        /**
+         * @hide
+         * @return
+         */
+        // TODO(jaewan): SystemApi
+        public ControllerInfoProvider getProvider() {
+            return mProvider;
+        }
+
+        @Override
+        public int hashCode() {
+            return mProvider.hashCode_impl();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ControllerInfo)) {
+                return false;
+            }
+            ControllerInfo other = (ControllerInfo) obj;
+            return mProvider.equals_impl(other.mProvider);
+        }
+
+        @Override
+        public String toString() {
+            return "ControllerInfo {pkg=" + getPackageName() + ", uid=" + getUid() + ", trusted="
+                    + isTrusted() + "}";
+        }
+    }
+
+    /**
+     * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
+     * <p>
+     * This intended behavior and here's the reasons.
+     *    1. Prevent multiple sessions with the same tag in a media app.
+     *       Whenever it happens only one session was properly setup and others were all dummies.
+     *       Android framework couldn't find the right session to dispatch media key event.
+     *    2. Simplify session's lifecycle.
+     *       {@link MediaSession} can be available after all of {@link MediaSession#setFlags(int)},
+     *       {@link MediaSession#setCallback(Callback)}, and
+     *       {@link MediaSession#setActive(boolean)}. It was common for an app to omit one, so
+     *       framework had to add heuristics to figure out if an app is
+     * @hide
+     */
+    private MediaSession2(Context context, MediaPlayerBase player, String id,
+            SessionCallback callback) {
+        super();
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaSession2(this, context, player, id, callback);
+    }
+
+    /**
+     * @hide
+     */
+    // TODO(jaewan): SystemApi
+    public MediaSession2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event to.
+     * Events from the {@link MediaController2} will be sent directly to the underlying
+     * player on the {@link Handler} where the session is created on.
+     * <p>
+     * If the new player is successfully set, {@link PlaybackListener}
+     * will be called to tell the current playback state of the new player.
+     * <p>
+     * Calling this method with {@code null} will disconnect binding connection between the
+     * controllers and also release this object.
+     *
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
+     *      It shouldn't be {@link MediaSession2} nor {@link MediaController2}.
+     * @throws IllegalArgumentException if the player is either {@link MediaSession2}
+     *      or {@link MediaController2}.
+     */
+    // TODO(jaewan): Add release instead of setPlayer(null).
+    public void setPlayer(MediaPlayerBase player) throws IllegalArgumentException {
+        mProvider.setPlayer_impl(player);
+    }
+
+    /**
+     * @return player
+     */
+    public @Nullable MediaPlayerBase getPlayer() {
+        return mProvider.getPlayer_impl();
+    }
+
+    /**
+     * Returns the {@link SessionToken} for creating {@link MediaController2}.
+     */
+    public @NonNull
+    SessionToken getToken() {
+        return mProvider.getToken_impl();
+    }
+
+    public @NonNull List<ControllerInfo> getConnectedControllers() {
+        return mProvider.getConnectedControllers_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @NonNull PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * underlying {@link MediaPlayerBase} which is previously set by
+     * {@link #setPlayer(MediaPlayerBase)}.
+     * <p>
+     * Added listeners will be also called when the underlying player is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Can handler be null? Follow API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
new file mode 100644
index 0000000..f1f5467
--- /dev/null
+++ b/media/java/android/media/MediaSessionService2.java
@@ -0,0 +1,264 @@
+/*
+ * 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.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSessionService2Provider;
+import android.os.IBinder;
+
+/**
+ * Service version of the {@link MediaSession2}.
+ * <p>
+ * It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
+ * to keep media playback in the background.
+ * <p>
+ * Here's the benefits of using {@link MediaSessionService2} instead of
+ * {@link MediaSession2}.
+ * <ul>
+ * <li>Another app can know that your app supports {@link MediaSession2} even when your app
+ * isn't running.
+ * <li>Another app can start playback of your app even when your app isn't running.
+ * </ul>
+ * For example, user's voice command can start playback of your app even when it's not running.
+ * <p>
+ * To use this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.session.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * A {@link MediaSessionService2} is another form of {@link MediaSession2}. IDs shouldn't
+ * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
+ * default, an empty string will be used for ID of the service. If you want to specify an ID,
+ * declare metadata in the manifest as follows.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.session.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ *   &lt;meta-data android:name="android.media.session"
+ *       android:value="session_id"/&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * It's recommended for an app to have a single {@link MediaSessionService2} declared in the
+ * manifest. Otherwise, your app might be shown twice in the list of the Auto/Wearable, or another
+ * app fails to pick the right session service when it wants to start the playback this app.
+ * <p>
+ * If there's conflicts with the session ID among the services, services wouldn't be available for
+ * any controllers.
+ * <p>
+ * Topic covered here:
+ * <ol>
+ * <li><a href="#ServiceLifecycle">Service Lifecycle</a>
+ * <li><a href="#Permissions">Permissions</a>
+ * </ol>
+ * <div class="special reference">
+ * <a name="ServiceLifecycle"></a>
+ * <h3>Service Lifecycle</h3>
+ * <p>
+ * Session service is bounded service. When a {@link MediaController2} is created for the
+ * session service, the controller binds to the session service. {@link #onCreateSession(String)}
+ * may be called after the {@link #onCreate} if the service hasn't created yet.
+ * <p>
+ * After the binding, session's {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}
+ * will be called to accept or reject connection request from a controller. If the connection is
+ * rejected, the controller will unbind. If it's accepted, the controller will be available to use
+ * and keep binding.
+ * <p>
+ * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState)}
+ * is called and service would become a foreground service. It's needed to keep playback after the
+ * controller is destroyed. The session service becomes background service when the playback is
+ * stopped.
+ * <a name="Permissions"></a>
+ * <h3>Permissions</h3>
+ * <p>
+ * Any app can bind to the session service with controller, but the controller can be used only if
+ * the session service accepted the connection request through
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
+ *
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Can we clean up sessions in onDestroy() automatically instead?
+//               What about currently running SessionCallback when the onDestroy() is called?
+// TODO(jaewan): Protect this with system|privilleged permission - Q.
+// TODO(jaewan): Add permission check for the service to know incoming connection request.
+//               Follow-up questions: What about asking a XML for list of white/black packages for
+//                                    allowing enumeration?
+//                                    We can read the information even when the service is started,
+//                                    so SessionManager.getXXXXService() can only return apps
+//                                    TODO(jaewan): Will be the black/white listing persistent?
+//                                                  In other words, can we cache the rejection?
+public abstract class MediaSessionService2 extends Service {
+    private final MediaSessionService2Provider mProvider;
+
+    /**
+     * This is the interface name that a service implementing a session service should say that it
+     * support -- that is, this is the action it uses for its intent filter.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.media.session.MediaSessionService2";
+
+    /**
+     * Name under which a MediaSessionService2 component publishes information about itself.
+     * This meta-data must provide a string value for the ID.
+     */
+    public static final String SERVICE_META_DATA = "android.media.session";
+
+    /**
+     * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
+     *
+     */
+    public static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
+
+    /**
+     * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
+     *
+     */
+    public static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
+
+    public MediaSessionService2() {
+        super();
+        mProvider = ApiLoader.getProvider(this).createMediaSessionService2(this);
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to initialize session service.
+     * <p>
+     * Override this method if you need your own initialization. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mProvider.onCreate_impl();
+    }
+
+    /**
+     * Called when another app requested to start this service to get {@link MediaSession2}.
+     * <p>
+     * Session service will accept or reject the connection with the
+     * {@link MediaSession2.SessionCallback} in the created session.
+     * <p>
+     * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
+     * expected ID that you've specified through the AndroidManifest.xml.
+     * <p>
+     * This method will be call on the main thread.
+     *
+     * @param sessionId session id written in the AndroidManifest.xml.
+     * @return a new session
+     * @see MediaSession2.Builder
+     * @see #getSession()
+     */
+    // TODO(jaewan): Replace this with onCreateSession(). Its sesssion callback will replace
+    //               this abstract method.
+    // TODO(jaewan): Should we also include/documents notification listener access?
+    // TODO(jaewan): Is term accepted/rejected correct? For permission, granted is more common.
+    // TODO(jaewan): Return ConnectResult() that encapsulate supported action and player.
+    public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
+
+    /**
+     * Called when the playback state of this session is changed, and notification needs update.
+     * <p>
+     * Default media style notification will be shown if you don't override this or return
+     * {@code null}. Override this method to show your own notification UI.
+     * <p>
+     * With the notification returned here, the service become foreground service when the playback
+     * is started. It becomes background service after the playback is stopped.
+     *
+     * @param state playback state
+     * @return a {@link MediaNotification}. If it's {@code null}, default notification will be shown
+     *     instead.
+     */
+    // TODO(jaewan): Also add metadata
+    public MediaNotification onUpdateNotification(PlaybackState state) {
+        return mProvider.onUpdateNotification_impl(state);
+    }
+
+    /**
+     * Get instance of the {@link MediaSession2} that you've previously created with the
+     * {@link #onCreateSession} for this service.
+     *
+     * @return created session
+     */
+    public final MediaSession2 getSession() {
+        return mProvider.getSession_impl();
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to handle incoming binding
+     * request. If the request is for getting the session, the intent will have action
+     * {@link #SERVICE_INTERFACE}.
+     * <p>
+     * Override this method if this service also needs to handle binder requests other than
+     * {@link #SERVICE_INTERFACE}. Derived classes MUST call through to the super class's
+     * implementation of this method.
+     *
+     * @param intent
+     * @return Binder
+     */
+    @CallSuper
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mProvider.onBind_impl(intent);
+    }
+
+    /**
+     * Returned by {@link #onUpdateNotification(PlaybackState)} for making session service
+     * foreground service to keep playback running in the background. It's highly recommended to
+     * show media style notification here.
+     */
+    // TODO(jaewan): Should we also move this to updatable?
+    public static class MediaNotification {
+        public final int id;
+        public final Notification notification;
+
+        private MediaNotification(int id, @NonNull Notification notification) {
+            this.id = id;
+            this.notification = notification;
+        }
+
+        /**
+         * Create a {@link MediaNotification}.
+         *
+         * @param notificationId notification id to be used for
+         *      {@link android.app.NotificationManager#notify(int, Notification)}.
+         * @param notification a notification to make session service foreground service. Media
+         *      style notification is recommended here.
+         * @return
+         */
+        public static MediaNotification create(int notificationId,
+                @NonNull Notification notification) {
+            if (notification == null) {
+                throw new IllegalArgumentException("Notification cannot be null");
+            }
+            return new MediaNotification(notificationId, notification);
+        }
+    }
+}
diff --git a/media/java/android/media/SessionToken.java b/media/java/android/media/SessionToken.java
new file mode 100644
index 0000000..b470dea
--- /dev/null
+++ b/media/java/android/media/SessionToken.java
@@ -0,0 +1,223 @@
+/*
+ * 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.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
+ * If it's representing a session service, it may not be ongoing.
+ * <p>
+ * This may be passed to apps by the session owner to allow them to create a
+ * {@link MediaController2} to communicate with the session.
+ * <p>
+ * It can be also obtained by {@link MediaSessionManager}.
+ * @hide
+ */
+// TODO(jaewan): Unhide. SessionToken2?
+// TODO(jaewan): Move Token to updatable!
+// TODO(jaewan): Find better name for this (SessionToken or Session2Token)
+public final class SessionToken {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
+    public @interface TokenType {
+    }
+
+    public static final int TYPE_SESSION = 0;
+    public static final int TYPE_SESSION_SERVICE = 1;
+
+    private static final String KEY_TYPE = "android.media.token.type";
+    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
+    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
+    private static final String KEY_ID = "android.media.token.id";
+    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final String mId;
+    private final IMediaSession2 mSessionBinder;
+
+    /**
+     * Constructor for the token.
+     *
+     * @hide
+     * @param type type
+     * @param packageName package name
+     * @param id id
+     * @param serviceName name of service. Can be {@code null} if it's not an service.
+     * @param sessionBinder binder for this session. Can be {@code null} if it's service.
+     * @hide
+     */
+    // TODO(jaewan): UID is also needed.
+    public SessionToken(@TokenType int type, @NonNull String packageName, @NonNull String id,
+            @Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) {
+        // TODO(jaewan): Add sanity check.
+        mType = type;
+        mPackageName = packageName;
+        mId = id;
+        mServiceName = serviceName;
+        mSessionBinder = sessionBinder;
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        return mType
+                + prime * (mPackageName.hashCode()
+                + prime * (mId.hashCode()
+                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
+                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0))));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SessionToken other = (SessionToken) obj;
+        if (!mPackageName.equals(other.getPackageName())
+                || !mServiceName.equals(other.getServiceName())
+                || !mId.equals(other.getId())
+                || mType != other.getType()) {
+            return false;
+        }
+        if (mSessionBinder == other.getSessionBinder()) {
+            return true;
+        } else if (mSessionBinder == null || other.getSessionBinder() == null) {
+            return false;
+        }
+        return mSessionBinder.asBinder().equals(other.getSessionBinder().asBinder());
+    }
+
+    @Override
+    public String toString() {
+        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
+                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+    }
+
+    /**
+     * @return package name
+     */
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return id
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * @return type of the token
+     * @see #TYPE_SESSION
+     * @see #TYPE_SESSION_SERVICE
+     */
+    public @TokenType int getType() {
+        return mType;
+    }
+
+    /**
+     * @return session binder.
+     * @hide
+     */
+    public @Nullable IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    /**
+     * @return service name if it's session service.
+     * @hide
+     */
+    public @Nullable String getServiceName() {
+        return mServiceName;
+    }
+
+    /**
+     * Create a token from the bundle, exported by {@link #toBundle()}.
+     *
+     * @param bundle
+     * @return
+     */
+    public static SessionToken fromBundle(@NonNull Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
+        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
+        final String id = bundle.getString(KEY_ID);
+        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
+
+        // Sanity check.
+        switch (type) {
+            case TYPE_SESSION:
+                if (!(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Session needs sessionBinder");
+                }
+                break;
+            case TYPE_SESSION_SERVICE:
+                if (TextUtils.isEmpty(serviceName)) {
+                    throw new IllegalArgumentException("Session service needs service name");
+                }
+                if (sessionBinder != null && !(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Invalid session binder");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type");
+        }
+        if (TextUtils.isEmpty(packageName) || id == null) {
+            throw new IllegalArgumentException("Package name nor ID cannot be null.");
+        }
+        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
+        //               controller's permission check. With it, sessionBinder should be available
+        //               if and only if for session, not session service.
+        return new SessionToken(type, packageName, id, serviceName,
+                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null);
+    }
+
+    /**
+     * Create a {@link Bundle} from this token to share it across processes.
+     *
+     * @return Bundle
+     * @hide
+     */
+    public Bundle toBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
+        bundle.putString(KEY_SERVICE_NAME, mServiceName);
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_TYPE, mType);
+        bundle.putBinder(KEY_SESSION_BINDER,
+                mSessionBinder != null ? mSessionBinder.asBinder() : null);
+        return bundle;
+    }
+}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 5fcb430..b8463dd 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,6 +17,7 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.IMediaSession2;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -49,4 +50,8 @@
     void setCallback(in ICallback callback);
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
+
+    // MediaSession2
+    Bundle createSessionToken(String callingPackage, String id, IMediaSession2 binder);
+    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index b215825..6a9f04a 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -24,8 +24,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.ISessionManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -38,6 +43,7 @@
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -331,6 +337,101 @@
     }
 
     /**
+     * Called when a {@link MediaSession2} is created.
+     *
+     * @hide
+     */
+    // TODO(jaewan): System API
+    public SessionToken createSessionToken(@NonNull String callingPackage, @NonNull String id,
+            @NonNull IMediaSession2 binder) {
+        try {
+            Bundle bundle = mService.createSessionToken(callingPackage, id, binder);
+            return SessionToken.fromBundle(bundle);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} whose sessions are active now. This list represents
+     * active sessions regardless of whether they're {@link MediaSession2} or
+     * {@link MediaSessionService2}.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getActiveSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} for {@link MediaSessionService2} regardless of their
+     * activeness. This list represents media apps that support background playback.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getSessionServiceTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get all {@link SessionToken}s. This is the combined list of {@link #getActiveSessionTokens()}
+     * and {@link #getSessionServiceTokens}.
+     *
+     * @return list of Tokens
+     * @see #getActiveSessionTokens
+     * @see #getSessionServiceTokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getAllSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    private static List<SessionToken> toTokenList(List<Bundle> bundles) {
+        List<SessionToken> tokens = new ArrayList<>();
+        if (bundles != null) {
+            for (int i = 0; i < bundles.size(); i++) {
+                SessionToken token = SessionToken.fromBundle(bundles.get(i));
+                if (token != null) {
+                    tokens.add(token);
+                }
+            }
+        }
+        return tokens;
+    }
+
+    /**
      * Check if the global priority session is currently active. This can be
      * used to decide if media keys should be sent to the session or to the app.
      *
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/media/java/android/media/update/MediaController2Provider.java
similarity index 61%
copy from packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
copy to media/java/android/media/update/MediaController2Provider.java
index dbc61c2..b15d6db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.core.instrumentation;
+package android.media.update;
 
-public interface Instrumentable {
+import android.media.SessionToken;
 
-    int METRICS_CATEGORY_UNKNOWN = 0;
-
-    /**
-     * Instrumented name for a view as defined in
-     * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}.
-     */
-    int getMetricsCategory();
+/**
+ * @hide
+ */
+public interface MediaController2Provider extends MediaPlayerBaseProvider {
+    void release_impl();
+    SessionToken getSessionToken_impl();
+    boolean isConnected_impl();
 }
diff --git a/media/java/android/media/update/MediaPlayerBaseProvider.java b/media/java/android/media/update/MediaPlayerBaseProvider.java
new file mode 100644
index 0000000..5b13e74
--- /dev/null
+++ b/media/java/android/media/update/MediaPlayerBaseProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.media.MediaPlayerBase;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * @hide
+ */
+public interface MediaPlayerBaseProvider {
+    void play_impl();
+    void pause_impl();
+    void stop_impl();
+    void skipToPrevious_impl();
+    void skipToNext_impl();
+
+    PlaybackState getPlaybackState_impl();
+    void addPlaybackListener_impl(MediaPlayerBase.PlaybackListener listener, Handler handler);
+    void removePlaybackListener_impl(MediaPlayerBase.PlaybackListener listener);
+}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
new file mode 100644
index 0000000..36fd182
--- /dev/null
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.media.MediaPlayerBase;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.SessionToken;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public interface MediaSession2Provider extends MediaPlayerBaseProvider {
+    void setPlayer_impl(MediaPlayerBase player) throws IllegalArgumentException;
+    MediaPlayerBase getPlayer_impl();
+    SessionToken getToken_impl();
+    List<ControllerInfo> getConnectedControllers_impl();
+
+    /**
+     * @hide
+     */
+    interface ControllerInfoProvider {
+        String getPackageName_impl();
+        int getUid_impl();
+        boolean isTrusted_impl();
+        int hashCode_impl();
+        boolean equals_impl(ControllerInfoProvider obj);
+    }
+}
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
new file mode 100644
index 0000000..1174915
--- /dev/null
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.content.Intent;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+public interface MediaSessionService2Provider {
+    MediaSession2 getSession_impl();
+    MediaNotification onUpdateNotification_impl(PlaybackState state);
+
+    // Service
+    void onCreate_impl();
+    IBinder onBind_impl(Intent intent);
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 1a0df52..91c9c66 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,11 +17,19 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.IMediaSession2Callback;
+import android.media.MediaController2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import java.util.concurrent.Executor;
+
 /**
  * Interface for connecting the public API to an updatable implementation.
  *
@@ -37,4 +45,15 @@
     VideoView2Provider createVideoView2(
             VideoView2 instance, ViewProvider superProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+    MediaSession2Provider createMediaSession2(MediaSession2 mediaSession2, Context context,
+            MediaPlayerBase player, String id, MediaSession2.SessionCallback callback);
+    MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
+            MediaSession2.ControllerInfo instance, Context context, int uid, int pid,
+            String packageName, IMediaSession2Callback callback);
+    MediaController2Provider createMediaController2(
+            MediaController2 instance, Context context, SessionToken token,
+            MediaController2.ControllerCallback callback, Executor executor);
+    MediaSessionService2Provider createMediaSessionService2(
+            MediaSessionService2 instance);
 }
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
index e542404..78c5b36 100644
--- a/media/java/android/media/update/ViewProvider.java
+++ b/media/java/android/media/update/ViewProvider.java
@@ -37,6 +37,8 @@
 // TODO @SystemApi
 public interface ViewProvider {
     // TODO Add more (all?) methods from View
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
     CharSequence getAccessibilityClassName_impl();
     boolean onTouchEvent_impl(MotionEvent ev);
     boolean onTrackballEvent_impl(MotionEvent ev);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index fadb76d..4c96d89 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -197,7 +197,7 @@
         assertFalse(metadata.isEmpty());
 
         CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
-                CameraCaptureSession.SESSION_ID_NONE);
+                CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null);
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b13de2e..042767d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -498,6 +498,8 @@
     <string name="wifi_display_certification">Wireless display certification</string>
     <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
     <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization">Connected MAC Randomization</string>
     <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
     <string name="mobile_data_always_on">Mobile data always active</string>
     <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -552,6 +554,8 @@
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
     <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
     <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization_summary">Randomize MAC address when connecting to Wi\u2011Fi networks</string>
     <!-- UI debug setting: limit size of Android logger buffers -->
     <string name="select_logd_size_title">Logger buffer sizes</string>
     <!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
@@ -1015,4 +1019,10 @@
     <!-- About phone, status item value if the actual value is not available. -->
     <string name="status_unavailable">Unavailable</string>
 
+    <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
+    <plurals name="wifi_tether_connected_summary">
+        <item quantity="one">%1$d device connected</item>
+        <item quantity="other">%1$d devices connected</item>
+    </plurals>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 764c5922..9b69304 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -128,14 +128,18 @@
 
     public boolean connect(BluetoothDevice device) {
         if (mService == null) return false;
-        List<BluetoothDevice> sinks = getConnectedDevices();
-        if (sinks != null) {
-            for (BluetoothDevice sink : sinks) {
-                if (sink.equals(device)) {
-                    Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
-                    continue;
+        int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+        if (max_connected_devices == 1) {
+            // Original behavior: disconnect currently connected device
+            List<BluetoothDevice> sinks = getConnectedDevices();
+            if (sinks != null) {
+                for (BluetoothDevice sink : sinks) {
+                    if (sink.equals(device)) {
+                        Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+                        continue;
+                    }
+                    mService.disconnect(sink);
                 }
-                mService.disconnect(sink);
             }
         }
         return mService.connect(device);
@@ -157,6 +161,16 @@
         return mService.getConnectionState(device);
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -180,8 +194,8 @@
     boolean isA2dpPlaying() {
         if (mService == null) return false;
         List<BluetoothDevice> sinks = mService.getConnectedDevices();
-        if (!sinks.isEmpty()) {
-            if (mService.isA2dpPlaying(sinks.get(0))) {
+        for (BluetoothDevice device : sinks) {
+            if (mService.isA2dpPlaying(device)) {
                 return true;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b49..ac3599c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@
     void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
     void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
     void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+    void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f57d02b..3cda9c9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
 
 package com.android.settingslib.bluetooth;
 
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +34,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -106,6 +110,12 @@
         // Dock event broadcasts
         addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
 
+        // Active device broadcasts
+        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
@@ -409,4 +419,35 @@
 
         return deviceAdded;
     }
+
+    private class ActiveDeviceChangedHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+                return;
+            }
+            CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            int bluetoothProfile = 0;
+            if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.A2DP;
+            } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.HEADSET;
+            } else {
+                Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+                return;
+            }
+            dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+        }
+    }
+
+    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                             int bluetoothProfile) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff10..fb0f75b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@
     private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
     private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
 
+    // Active device state
+    private boolean mIsActiveDeviceA2dp = false;
+    private boolean mIsActiveDeviceHeadset = false;
+
     /**
      * Describes the current device and profile for logging.
      *
@@ -156,6 +160,7 @@
             mRemovedProfiles.add(profile);
             mLocalNapRoleConnected = false;
         }
+        fetchActiveDevices();
     }
 
     CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
         fetchName();
         fetchBtClass();
         updateProfiles();
+        fetchActiveDevices();
         migratePhonebookPermissionChoice();
         migrateMessagePermissionChoice();
         fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
         return mDevice.getBondState();
     }
 
+    /**
+     * Set the device status as active or non-active per Bluetooth profile.
+     *
+     * @param isActive true if the device is active
+     * @param bluetoothProfile the Bluetooth profile
+     */
+    public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+        boolean changed = false;
+        switch (bluetoothProfile) {
+        case BluetoothProfile.A2DP:
+            changed = (mIsActiveDeviceA2dp != isActive);
+            mIsActiveDeviceA2dp = isActive;
+            break;
+        case BluetoothProfile.HEADSET:
+            changed = (mIsActiveDeviceHeadset != isActive);
+            mIsActiveDeviceHeadset = isActive;
+            break;
+        default:
+            Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+                    " isActive " + isActive);
+            break;
+        }
+        if (changed) {
+            dispatchAttributesChanged();
+        }
+    }
+
     void setRssi(short rssi) {
         if (mRssi != rssi) {
             mRssi = rssi;
@@ -529,6 +562,17 @@
         return true;
     }
 
+    private void fetchActiveDevices() {
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null) {
+            mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if (headsetProfile != null) {
+            mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+        }
+    }
+
     /**
      * Refreshes the UI for the BT class, including fetching the latest value
      * for the class.
@@ -896,37 +940,60 @@
                     com.android.settingslib.Utils.formatPercentage(batteryLevel);
         }
 
+        // TODO: A temporary workaround solution using string description the device is active.
+        // Issue tracked by b/72317067 .
+        // An alternative solution would be visual indication.
+        // Intentionally not adding the strings to strings.xml for now:
+        //  1) If this is just a short-term solution, no need to waste translation effort
+        //  2) The number of strings with all possible combinations becomes enormously large.
+        // If string description becomes part of the final solution, we MUST NOT
+        // concatenate the strings here: this does not translate well.
+        String activeString = null;
+        if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+            activeString = ", active";
+        } else {
+            if (mIsActiveDeviceA2dp) {
+                activeString = ", active(media)";
+            }
+            if (mIsActiveDeviceHeadset) {
+                activeString = ", active(phone)";
+            }
+        }
+        if (activeString == null) activeString = "";
+
         if (profileConnected) {
             if (a2dpNotConnected && hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(
                             R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
+                        activeString;
                 }
 
             } else if (a2dpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
                 }
 
             } else if (hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset)
+                          + activeString;
                 }
             } else {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected);
+                    return mContext.getString(R.string.bluetooth_connected) + activeString;
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d45fe1a..ee12191 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 22674cb..cda4e45 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -239,4 +239,8 @@
     public BluetoothDevice getRemoteDevice(String address) {
         return mAdapter.getRemoteDevice(address);
     }
+
+    public int getMaxConnectedAudioDevices() {
+        return mAdapter.getMaxConnectedAudioDevices();
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
deleted file mode 100644
index 7227304..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-/**
- * {@link LogWriter} that writes data to eventlog.
- */
-public class EventLogWriter implements LogWriter {
-
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
-
-    public void visible(Context context, int source, int category) {
-        final LogMaker logMaker = new LogMaker(category)
-                .setType(MetricsProto.MetricsEvent.TYPE_OPEN)
-                .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
-        MetricsLogger.action(logMaker);
-    }
-
-    public void hidden(Context context, int category) {
-        MetricsLogger.hidden(context, category);
-    }
-
-    public void action(int category, int value, Pair<Integer, Object>... taggedData) {
-        if (taggedData == null || taggedData.length == 0) {
-            mMetricsLogger.action(category, value);
-        } else {
-            final LogMaker logMaker = new LogMaker(category)
-                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
-                    .setSubtype(value);
-            for (Pair<Integer, Object> pair : taggedData) {
-                logMaker.addTaggedData(pair.first, pair.second);
-            }
-            mMetricsLogger.write(logMaker);
-        }
-    }
-
-    public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
-        action(category, value ? 1 : 0, taggedData);
-    }
-
-    public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
-        action(context, category, "", taggedData);
-    }
-
-    public void actionWithSource(Context context, int source, int category) {
-        final LogMaker logMaker = new LogMaker(category)
-                .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
-        if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) {
-            logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source);
-        }
-        MetricsLogger.action(logMaker);
-    }
-
-    /** @deprecated use {@link #action(int, int, Pair[])} */
-    @Deprecated
-    public void action(Context context, int category, int value) {
-        MetricsLogger.action(context, category, value);
-    }
-
-    /** @deprecated use {@link #action(int, boolean, Pair[])} */
-    @Deprecated
-    public void action(Context context, int category, boolean value) {
-        MetricsLogger.action(context, category, value);
-    }
-
-    public void action(Context context, int category, String pkg,
-            Pair<Integer, Object>... taggedData) {
-        if (taggedData == null || taggedData.length == 0) {
-            MetricsLogger.action(context, category, pkg);
-        } else {
-            final LogMaker logMaker = new LogMaker(category)
-                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
-                    .setPackageName(pkg);
-            for (Pair<Integer, Object> pair : taggedData) {
-                logMaker.addTaggedData(pair.first, pair.second);
-            }
-            MetricsLogger.action(logMaker);
-        }
-    }
-
-    public void count(Context context, String name, int value) {
-        MetricsLogger.count(context, name, value);
-    }
-
-    public void histogram(Context context, String name, int bucket) {
-        MetricsLogger.histogram(context, name, bucket);
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
deleted file mode 100644
index 4b9f572..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import android.content.Context;
-import android.util.Pair;
-
-/**
- * Generic log writer interface.
- */
-public interface LogWriter {
-
-    /**
-     * Logs a visibility event when view becomes visible.
-     */
-    void visible(Context context, int source, int category);
-
-    /**
-     * Logs a visibility event when view becomes hidden.
-     */
-    void hidden(Context context, int category);
-
-    /**
-     * Logs a user action.
-     */
-    void action(int category, int value, Pair<Integer, Object>... taggedData);
-
-    /**
-     * Logs a user action.
-     */
-    void action(int category, boolean value, Pair<Integer, Object>... taggedData);
-
-    /**
-     * Logs an user action.
-     */
-    void action(Context context, int category, Pair<Integer, Object>... taggedData);
-
-    /**
-     * Logs an user action.
-     */
-    void actionWithSource(Context context, int source, int category);
-
-    /**
-     * Logs an user action.
-     * @deprecated use {@link #action(int, int, Pair[])}
-     */
-    @Deprecated
-    void action(Context context, int category, int value);
-
-    /**
-     * Logs an user action.
-     * @deprecated use {@link #action(int, boolean, Pair[])}
-     */
-    @Deprecated
-    void action(Context context, int category, boolean value);
-
-    /**
-     * Logs an user action.
-     */
-    void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData);
-
-    /**
-     * Logs a count.
-     */
-    void count(Context context, String name, int value);
-
-    /**
-     * Logs a histogram event.
-     */
-    void histogram(Context context, String name, int bucket);
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
deleted file mode 100644
index 1e5b378..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * FeatureProvider for metrics.
- */
-public class MetricsFeatureProvider {
-    private List<LogWriter> mLoggerWriters;
-
-    public MetricsFeatureProvider() {
-        mLoggerWriters = new ArrayList<>();
-        installLogWriters();
-    }
-
-    protected void installLogWriters() {
-        mLoggerWriters.add(new EventLogWriter());
-    }
-
-    public void visible(Context context, int source, int category) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.visible(context, source, category);
-        }
-    }
-
-    public void hidden(Context context, int category) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.hidden(context, category);
-        }
-    }
-
-    public void actionWithSource(Context context, int source, int category) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.actionWithSource(context, source, category);
-        }
-    }
-
-    /**
-     * Logs a user action. Includes the elapsed time since the containing
-     * fragment has been visible.
-     */
-    public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(category, value,
-                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
-        }
-    }
-
-    /**
-     * Logs a user action. Includes the elapsed time since the containing
-     * fragment has been visible.
-     */
-    public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(category, value,
-                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
-        }
-    }
-
-    public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(context, category, taggedData);
-        }
-    }
-
-    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */
-    @Deprecated
-    public void action(Context context, int category, int value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(context, category, value);
-        }
-    }
-
-    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */
-    @Deprecated
-    public void action(Context context, int category, boolean value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(context, category, value);
-        }
-    }
-
-    public void action(Context context, int category, String pkg,
-            Pair<Integer, Object>... taggedData) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(context, category, pkg, taggedData);
-        }
-    }
-
-    public void count(Context context, String name, int value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.count(context, name, value);
-        }
-    }
-
-    public void histogram(Context context, String name, int bucket) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.histogram(context, name, bucket);
-        }
-    }
-
-    public int getMetricsCategory(Object object) {
-        if (object == null || !(object instanceof Instrumentable)) {
-            return MetricsEvent.VIEW_UNKNOWN;
-        }
-        return ((Instrumentable) object).getMetricsCategory();
-    }
-
-    public void logDashboardStartIntent(Context context, Intent intent,
-            int sourceMetricsCategory) {
-        if (intent == null) {
-            return;
-        }
-        final ComponentName cn = intent.getComponent();
-        if (cn == null) {
-            final String action = intent.getAction();
-            if (TextUtils.isEmpty(action)) {
-                // Not loggable
-                return;
-            }
-            action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
-                    Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
-            return;
-        } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) {
-            // Going to a Setting internal page, skip click logging in favor of page's own
-            // visibility logging.
-            return;
-        }
-        action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
-                Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
-    }
-
-    private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) {
-        return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
deleted file mode 100644
index facce4e..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentSkipListSet;
-
-public class SharedPreferencesLogger implements SharedPreferences {
-
-    private static final String LOG_TAG = "SharedPreferencesLogger";
-
-    private final String mTag;
-    private final Context mContext;
-    private final MetricsFeatureProvider mMetricsFeature;
-    private final Set<String> mPreferenceKeySet;
-
-    public SharedPreferencesLogger(Context context, String tag,
-            MetricsFeatureProvider metricsFeature) {
-        mContext = context;
-        mTag = tag;
-        mMetricsFeature = metricsFeature;
-        mPreferenceKeySet = new ConcurrentSkipListSet<>();
-    }
-
-    @Override
-    public Map<String, ?> getAll() {
-        return null;
-    }
-
-    @Override
-    public String getString(String key, @Nullable String defValue) {
-        return defValue;
-    }
-
-    @Override
-    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
-        return defValues;
-    }
-
-    @Override
-    public int getInt(String key, int defValue) {
-        return defValue;
-    }
-
-    @Override
-    public long getLong(String key, long defValue) {
-        return defValue;
-    }
-
-    @Override
-    public float getFloat(String key, float defValue) {
-        return defValue;
-    }
-
-    @Override
-    public boolean getBoolean(String key, boolean defValue) {
-        return defValue;
-    }
-
-    @Override
-    public boolean contains(String key) {
-        return false;
-    }
-
-    @Override
-    public Editor edit() {
-        return new EditorLogger();
-    }
-
-    @Override
-    public void registerOnSharedPreferenceChangeListener(
-            OnSharedPreferenceChangeListener listener) {
-    }
-
-    @Override
-    public void unregisterOnSharedPreferenceChangeListener(
-            OnSharedPreferenceChangeListener listener) {
-    }
-
-    private void logValue(String key, Object value) {
-        logValue(key, value, false /* forceLog */);
-    }
-
-    private void logValue(String key, Object value, boolean forceLog) {
-        final String prefKey = buildPrefKey(mTag, key);
-        if (!forceLog && !mPreferenceKeySet.contains(prefKey)) {
-            // Pref key doesn't exist in set, this is initial display so we skip metrics but
-            // keeps track of this key.
-            mPreferenceKeySet.add(prefKey);
-            return;
-        }
-        // TODO: Remove count logging to save some resource.
-        mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1);
-
-        final Pair<Integer, Object> valueData;
-        if (value instanceof Long) {
-            final Long longVal = (Long) value;
-            final int intVal;
-            if (longVal > Integer.MAX_VALUE) {
-                intVal = Integer.MAX_VALUE;
-            } else if (longVal < Integer.MIN_VALUE) {
-                intVal = Integer.MIN_VALUE;
-            } else {
-                intVal = longVal.intValue();
-            }
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    intVal);
-        } else if (value instanceof Integer) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    value);
-        } else if (value instanceof Boolean) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    (Boolean) value ? 1 : 0);
-        } else if (value instanceof Float) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE,
-                    value);
-        } else if (value instanceof String) {
-            Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value);
-            valueData = null;
-        } else {
-            Log.w(LOG_TAG, "Tried to log unloggable object" + value);
-            valueData = null;
-        }
-        if (valueData != null) {
-            // Pref key exists in set, log it's change in metrics.
-            mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE,
-                    Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey),
-                    valueData);
-        }
-    }
-
-    @VisibleForTesting
-    void logPackageName(String key, String value) {
-        final String prefKey = mTag + "/" + key;
-        mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value,
-                Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey));
-    }
-
-    private void safeLogValue(String key, String value) {
-        new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value);
-    }
-
-    public static String buildCountName(String prefKey, Object value) {
-        return prefKey + "|" + value;
-    }
-
-    public static String buildPrefKey(String tag, String key) {
-        return tag + "/" + key;
-    }
-
-    private class AsyncPackageCheck extends AsyncTask<String, Void, Void> {
-        @Override
-        protected Void doInBackground(String... params) {
-            String key = params[0];
-            String value = params[1];
-            PackageManager pm = mContext.getPackageManager();
-            try {
-                // Check if this might be a component.
-                ComponentName name = ComponentName.unflattenFromString(value);
-                if (value != null) {
-                    value = name.getPackageName();
-                }
-            } catch (Exception e) {
-            }
-            try {
-                pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER);
-                logPackageName(key, value);
-            } catch (PackageManager.NameNotFoundException e) {
-                // Clearly not a package, and it's unlikely this preference is in prefSet, so
-                // lets force log it.
-                logValue(key, value, true /* forceLog */);
-            }
-            return null;
-        }
-    }
-
-    public class EditorLogger implements Editor {
-        @Override
-        public Editor putString(String key, @Nullable String value) {
-            safeLogValue(key, value);
-            return this;
-        }
-
-        @Override
-        public Editor putStringSet(String key, @Nullable Set<String> values) {
-            safeLogValue(key, TextUtils.join(",", values));
-            return this;
-        }
-
-        @Override
-        public Editor putInt(String key, int value) {
-            logValue(key, value);
-            return this;
-        }
-
-        @Override
-        public Editor putLong(String key, long value) {
-            logValue(key, value);
-            return this;
-        }
-
-        @Override
-        public Editor putFloat(String key, float value) {
-            logValue(key, value);
-            return this;
-        }
-
-        @Override
-        public Editor putBoolean(String key, boolean value) {
-            logValue(key, value);
-            return this;
-        }
-
-        @Override
-        public Editor remove(String key) {
-            return this;
-        }
-
-        @Override
-        public Editor clear() {
-            return this;
-        }
-
-        @Override
-        public boolean commit() {
-            return true;
-        }
-
-        @Override
-        public void apply() {
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
deleted file mode 100644
index 7983896..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.core.instrumentation;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-import android.os.SystemClock;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
-
-import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
-
-/**
- * Logs visibility change of a fragment.
- */
-public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause {
-
-    private static final String TAG = "VisibilityLoggerMixin";
-
-    private final int mMetricsCategory;
-
-    private MetricsFeatureProvider mMetricsFeature;
-    private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
-    private long mVisibleTimestamp;
-
-    /**
-     * The metrics category constant for logging source when a setting fragment is opened.
-     */
-    public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics";
-
-    private VisibilityLoggerMixin() {
-        mMetricsCategory = METRICS_CATEGORY_UNKNOWN;
-    }
-
-    public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
-        mMetricsCategory = metricsCategory;
-        mMetricsFeature = metricsFeature;
-    }
-
-    @Override
-    public void onResume() {
-        mVisibleTimestamp = SystemClock.elapsedRealtime();
-        if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
-            mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory);
-        }
-    }
-
-    @Override
-    public void onPause() {
-        mVisibleTimestamp = 0;
-        if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
-            mMetricsFeature.hidden(null /* context */, mMetricsCategory);
-        }
-    }
-
-    /**
-     * Sets source metrics category for this logger. Source is the caller that opened this UI.
-     */
-    public void setSourceMetricsCategory(Activity activity) {
-        if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) {
-            return;
-        }
-        final Intent intent = activity.getIntent();
-        if (intent == null) {
-            return;
-        }
-        mSourceMetricsCategory = intent.getIntExtra(EXTRA_SOURCE_METRICS_CATEGORY,
-                MetricsProto.MetricsEvent.VIEW_UNKNOWN);
-    }
-
-    /** Returns elapsed time since onResume() */
-    public long elapsedTimeSinceVisible() {
-        if (mVisibleTimestamp == 0) {
-            return 0;
-        }
-        return SystemClock.elapsedRealtime() - mVisibleTimestamp;
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
deleted file mode 100644
index 8bea51d..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.TestConfig;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class MetricsFeatureProviderTest {
-    private static int CATEGORY = 10;
-    private static boolean SUBTYPE_BOOLEAN = true;
-    private static int SUBTYPE_INTEGER = 1;
-    private static long ELAPSED_TIME = 1000;
-
-    @Mock private LogWriter mockLogWriter;
-    @Mock private VisibilityLoggerMixin mockVisibilityLogger;
-
-    private Context mContext;
-    private MetricsFeatureProvider mProvider;
-
-    @Captor
-    private ArgumentCaptor<Pair> mPairCaptor;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mProvider = new MetricsFeatureProvider();
-        List<LogWriter> writers = new ArrayList<>();
-        writers.add(mockLogWriter);
-        ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
-
-        when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME);
-    }
-
-    @Test
-    public void logDashboardStartIntent_intentEmpty_shouldNotLog() {
-        mProvider.logDashboardStartIntent(mContext, null /* intent */,
-                MetricsEvent.SETTINGS_GESTURES);
-
-        verifyNoMoreInteractions(mockLogWriter);
-    }
-
-    @Test
-    public void logDashboardStartIntent_intentHasNoComponent_shouldLog() {
-        final Intent intent = new Intent(Intent.ACTION_ASSIST);
-
-        mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
-
-        verify(mockLogWriter).action(
-                eq(mContext),
-                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
-                anyString(),
-                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
-    }
-
-    @Test
-    public void logDashboardStartIntent_intentIsExternal_shouldLog() {
-        final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
-        mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
-
-        verify(mockLogWriter).action(
-                eq(mContext),
-                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
-                anyString(),
-                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
-    }
-
-    @Test
-    public void action_BooleanLogsElapsedTime() {
-        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN);
-        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture());
-
-        Pair value = mPairCaptor.getValue();
-        assertThat(value.first instanceof Integer).isTrue();
-        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
-        assertThat(value.second).isEqualTo(ELAPSED_TIME);
-    }
-
-    @Test
-    public void action_IntegerLogsElapsedTime() {
-        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER);
-        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture());
-
-        Pair value = mPairCaptor.getValue();
-        assertThat(value.first instanceof Integer).isTrue();
-        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
-        assertThat(value.second).isEqualTo(ELAPSED_TIME);
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
deleted file mode 100644
index d558a64..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Pair;
-
-import com.android.settingslib.TestConfig;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import com.google.common.truth.Platform;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SharedPreferenceLoggerTest {
-
-    private static final String TEST_TAG = "tag";
-    private static final String TEST_KEY = "key";
-
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
-
-    private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher;
-    @Mock
-    private MetricsFeatureProvider mMetricsFeature;
-    private SharedPreferencesLogger mSharedPrefLogger;
-
-    @Before
-    public void init() {
-        MockitoAnnotations.initMocks(this);
-        mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature);
-        mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class);
-    }
-
-    @Test
-    public void putInt_shouldNotLogInitialPut() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        editor.putInt(TEST_KEY, 1);
-        editor.putInt(TEST_KEY, 1);
-        editor.putInt(TEST_KEY, 1);
-        editor.putInt(TEST_KEY, 2);
-        editor.putInt(TEST_KEY, 2);
-        editor.putInt(TEST_KEY, 2);
-        editor.putInt(TEST_KEY, 2);
-
-        verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
-    }
-
-    @Test
-    public void putBoolean_shouldNotLogInitialPut() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        editor.putBoolean(TEST_KEY, true);
-        editor.putBoolean(TEST_KEY, true);
-        editor.putBoolean(TEST_KEY, false);
-        editor.putBoolean(TEST_KEY, false);
-        editor.putBoolean(TEST_KEY, false);
-
-
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true)));
-        verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false)));
-    }
-
-    @Test
-    public void putLong_shouldNotLogInitialPut() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, 2);
-
-        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
-    }
-
-    @Test
-    public void putLong_biggerThanIntMax_shouldLogIntMax() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        final long veryBigNumber = 500L + Integer.MAX_VALUE;
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, veryBigNumber);
-
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(
-                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE)));
-    }
-
-    @Test
-    public void putLong_smallerThanIntMin_shouldLogIntMin() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        final long veryNegativeNumber = -500L + Integer.MIN_VALUE;
-        editor.putLong(TEST_KEY, 1);
-        editor.putLong(TEST_KEY, veryNegativeNumber);
-
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(
-                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE)));
-    }
-
-    @Test
-    public void putFloat_shouldNotLogInitialPut() {
-        final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
-        editor.putFloat(TEST_KEY, 1);
-        editor.putFloat(TEST_KEY, 1);
-        editor.putFloat(TEST_KEY, 1);
-        editor.putFloat(TEST_KEY, 1);
-        editor.putFloat(TEST_KEY, 2);
-
-        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class)));
-    }
-
-    @Test
-    public void logPackage_shouldUseLogPackageApi() {
-        mSharedPrefLogger.logPackageName("key", "com.android.settings");
-        verify(mMetricsFeature).action(any(Context.class),
-                eq(ACTION_SETTINGS_PREFERENCE_CHANGE),
-                eq("com.android.settings"),
-                any(Pair.class));
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) {
-        return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz);
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) {
-        return pair -> pair.first == tag
-                && Platform.isInstanceOfType(pair.second, Integer.class)
-                && pair.second.equals((bool ? 1 : 0));
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) {
-        return pair -> pair.first == tag
-                && Platform.isInstanceOfType(pair.second, Integer.class)
-                && pair.second.equals(val);
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
deleted file mode 100644
index a264886..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.core.instrumentation;
-
-import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
-
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.TestConfig;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class VisibilityLoggerMixinTest {
-
-    @Mock
-    private MetricsFeatureProvider mMetricsFeature;
-
-    private VisibilityLoggerMixin mMixin;
-
-    @Before
-    public void init() {
-        MockitoAnnotations.initMocks(this);
-        mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature);
-    }
-
-    @Test
-    public void shouldLogVisibleOnResume() {
-        mMixin.onResume();
-
-        verify(mMetricsFeature, times(1))
-                .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN),
-                        eq(TestInstrumentable.TEST_METRIC));
-    }
-
-    @Test
-    public void shouldLogVisibleWithSource() {
-        final Intent sourceIntent = new Intent()
-                .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY,
-                        MetricsProto.MetricsEvent.SETTINGS_GESTURES);
-        final Activity activity = mock(Activity.class);
-        when(activity.getIntent()).thenReturn(sourceIntent);
-        mMixin.setSourceMetricsCategory(activity);
-        mMixin.onResume();
-
-        verify(mMetricsFeature, times(1))
-                .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES),
-                        eq(TestInstrumentable.TEST_METRIC));
-    }
-
-    @Test
-    public void shouldLogHideOnPause() {
-        mMixin.onPause();
-
-        verify(mMetricsFeature, times(1))
-                .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC));
-    }
-
-    @Test
-    public void shouldNotLogIfMetricsFeatureIsNull() {
-        mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null);
-        mMixin.onResume();
-        mMixin.onPause();
-
-        verify(mMetricsFeature, never())
-                .hidden(nullable(Context.class), anyInt());
-    }
-
-    @Test
-    public void shouldNotLogIfMetricsCategoryIsUnknown() {
-        mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature);
-
-        mMixin.onResume();
-        mMixin.onPause();
-
-        verify(mMetricsFeature, never())
-                .hidden(nullable(Context.class), anyInt());
-    }
-
-    private final class TestInstrumentable implements Instrumentable {
-
-        public static final int TEST_METRIC = 12345;
-
-        @Override
-        public int getMetricsCategory() {
-            return TEST_METRIC;
-        }
-    }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 87ed7eb..5a75681 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1128,6 +1128,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_FIRST_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG);
+        dumpSetting(s, p,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+                    GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED);
     }
 
     /** Dump a single {@link SettingsState.Setting} to a proto buf */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 5e09e75..d0389eb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -31,7 +31,7 @@
     <TextView android:id="@+id/title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginBottom="@dimen/widget_vertical_padding"
+              android:layout_marginBottom="7dp"
               android:paddingStart="64dp"
               android:paddingEnd="64dp"
               android:theme="@style/TextAppearance.Keyguard"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 9adb550..eda8c69 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -44,6 +44,7 @@
                 android:layout_gravity="center_horizontal"
                 android:layout_centerHorizontal="true"
                 android:layout_alignParentTop="true"
+                android:letterSpacing="0.04"
                 android:textColor="?attr/wallpaperTextColor"
                 android:singleLine="true"
                 style="@style/widget_big_thin"
@@ -52,16 +53,16 @@
                 android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
             <View
                 android:id="@+id/clock_separator"
-                android:layout_width="16dp"
-                android:layout_height="1dp"
-                android:layout_marginTop="10dp"
+                android:layout_width="@dimen/widget_separator_width"
+                android:layout_height="@dimen/widget_separator_thickness"
+                android:layout_marginTop="22dp"
                 android:layout_below="@id/clock_view"
                 android:background="#f00"
                 android:layout_centerHorizontal="true" />
 
             <include layout="@layout/keyguard_status_area"
                 android:id="@+id/keyguard_status_area"
-                android:layout_marginTop="10dp"
+                android:layout_marginTop="20dp"
                 android:layout_marginBottom="@dimen/widget_vertical_padding"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 04cf6b0..5aca7f9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,7 +42,7 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Default clock parameters -->
-    <dimen name="bottom_text_spacing_digital">-10dp</dimen>
+    <dimen name="bottom_text_spacing_digital">-24dp</dimen>
     <!-- Slice header -->
     <dimen name="widget_title_font_size">28sp</dimen>
     <!-- Slice subtitle  -->
@@ -52,12 +52,16 @@
     <!-- Clock with header -->
     <dimen name="widget_small_font_size">22dp</dimen>
     <!-- Dash between clock and header -->
-    <dimen name="widget_vertical_padding">16dp</dimen>
+    <dimen name="widget_separator_width">16dp</dimen>
+    <dimen name="widget_separator_thickness">1dp</dimen>
+    <dimen name="widget_vertical_padding">26dp</dimen>
+    <dimen name="widget_icon_bottom_padding">14dp</dimen>
     <!-- Subtitle paddings -->
-    <dimen name="widget_separator_thickness">2dp</dimen>
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
     <dimen name="widget_icon_padding">8dp</dimen>
+    <!-- Dash between notification shelf and date/alarm -->
+    <dimen name="widget_bottom_separator_padding">29dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml
index c19fa08..24afebd 100644
--- a/packages/SystemUI/res/color/qs_background_dark.xml
+++ b/packages/SystemUI/res/color/qs_background_dark.xml
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.87"
+    <item android:alpha="1"
           android:color="?android:attr/colorBackgroundFloating"/>
 </selector>
diff --git a/packages/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
new file mode 100644
index 0000000..d901292
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_bg_gradient.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
new file mode 100644
index 0000000..a1ad528
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:angle="270"
+        android:startColor="#ff000000"
+        android:endColor="#00000000"
+        android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 59c0957..e52aa14 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -25,6 +25,6 @@
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
         android:textColor="?android:attr/textColorPrimary"
         android:gravity="center_vertical|start"
-        android:paddingEnd="@dimen/battery_level_padding_start"
+        android:paddingStart="@dimen/battery_level_padding_start"
         android:importantForAccessibility="no"
         />
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac6..b3b6a0c 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,6 +20,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="0dp"
+    android:elevation="4dp"
     android:orientation="vertical"
     android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1fd239b..0b9a7b2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,8 @@
     android:clickable="true"
     android:orientation="vertical"
     android:paddingBottom="8dp"
-    android:visibility="invisible">
+    android:visibility="invisible"
+    android:elevation="4dp" >
 
     <include
         android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3d09b74..9f6a946 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -21,6 +21,7 @@
     android:id="@+id/qs_footer"
     android:layout_width="match_parent"
     android:layout_height="48dp"
+    android:elevation="4dp"
     android:baselineAligned="false"
     android:clickable="false"
     android:clipChildren="false"
@@ -105,17 +106,6 @@
                 android:visibility="invisible"/>
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
-        <com.android.systemui.statusbar.phone.ExpandableIndicator
-            android:id="@+id/expand_indicator"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:clipToPadding="false"
-            android:clickable="true"
-            android:focusable="true"
-            android:background="?android:attr/selectableItemBackgroundBorderless"
-            android:contentDescription="@string/accessibility_quick_settings_expand"
-            android:padding="14dp" />
     </LinearLayout>
 
 </com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 5541f3d..5bcb7fd 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,17 +18,46 @@
     android:id="@+id/quick_settings_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/qs_background_primary"
-    android:elevation="4dp"
     android:clipToPadding="false"
-    android:clipChildren="false">
+    android:clipChildren="false" >
+
+    <!-- Main QS background -->
+    <View
+        android:id="@+id/quick_settings_background"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:elevation="4dp"
+        android:background="@drawable/qs_background_primary" />
+
+    <!-- Black part behind the status bar -->
+    <View
+        android:id="@+id/quick_settings_status_bar_background"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_header_system_icons_area_height"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:background="#ff000000" />
+
+    <!-- Gradient view behind QS -->
+    <View
+        android:id="@+id/quick_settings_gradient_view"
+        android:layout_width="match_parent"
+        android:layout_height="126dp"
+        android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:background="@drawable/qs_bg_gradient" />
+
 
     <com.android.systemui.qs.QSPanel
         android:id="@+id/quick_settings_panel"
-        android:layout_marginTop="28dp"
+        android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="48dp" />
+        android:layout_marginBottom="48dp"
+        android:elevation="4dp"
+    />
+
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
new file mode 100644
index 0000000..e0f0ed9
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/quick_qs_status_icons"
+    android:layout_width="match_parent"
+    android:layout_height="20dp"
+    android:layout_marginBottom="22dp"
+    android:layout_below="@id/quick_status_bar_system_icons"
+    >
+
+</View>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index e8b418c..dacc3f9 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -28,23 +28,21 @@
     android:clipToPadding="false"
     android:paddingTop="0dp"
     android:paddingEnd="0dp"
-    android:paddingStart="0dp">
+    android:paddingStart="0dp"
+    android:elevation="4dp" >
 
     <include layout="@layout/quick_status_bar_header_system_icons" />
+    <include layout="@layout/quick_qs_status_icons" />
 
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
         android:layout_width="match_parent"
         android:layout_height="48dp"
-        android:layout_alignParentEnd="true"
-        android:layout_marginTop="31dp"
-        android:layout_alignParentTop="true"
+        android:layout_below="@id/quick_qs_status_icons"
         android:accessibilityTraversalAfter="@+id/date_time_group"
         android:accessibilityTraversalBefore="@id/expand_indicator"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:layout_marginStart="8dp"
-        android:layout_marginEnd="8dp"
         android:focusable="true"
         android:importantForAccessibility="yes" />
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 739a255..2c69501 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -17,6 +17,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/quick_status_bar_system_icons"
     android:layout_width="match_parent"
     android:layout_height="@dimen/qs_header_system_icons_area_height"
     android:layout_alignParentEnd="true"
@@ -40,6 +41,17 @@
         systemui:showDark="false"
     />
 
+    <com.android.systemui.statusbar.policy.DateView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="4dp"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+        android:textSize="@dimen/qs_time_collapsed_size"
+        android:gravity="center_vertical"
+        systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
     <android.widget.Space
         android:id="@+id/space"
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 7762950..5074682 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,19 +14,13 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="@dimen/navigation_side_padding"
+<com.android.systemui.statusbar.policy.KeyButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/rotate_suggestion"
+    android:layout_width="@dimen/navigation_extra_key_width"
     android:layout_height="match_parent"
-    android:layout_weight="0"
-    >
-    <com.android.systemui.statusbar.policy.KeyButtonView
-        android:id="@+id/rotate_suggestion"
-        android:layout_width="@dimen/navigation_extra_key_width"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="2dp"
-        android:visibility="invisible"
-        android:scaleType="centerInside"
-    />
-    <!-- TODO android:contentDescription -->
-</FrameLayout>
+    android:layout_marginEnd="2dp"
+    android:visibility="invisible"
+    android:scaleType="centerInside"
+    android:contentDescription="@string/accessibility_rotate_button"
+/>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 17b38cb..8c0b9ba 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -54,30 +54,47 @@
             android:layout_height="match_parent"
             android:layout="@layout/operator_name" />
 
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_width="wrap_content"
+        <LinearLayout
             android:layout_height="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-            android:gravity="center_vertical|start"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+        >
+            <com.android.systemui.statusbar.policy.Clock
+                android:id="@+id/clock"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                android:singleLine="true"
+                android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+                android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+                android:gravity="center_vertical|start"
+            />
+
+            <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+                 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+                android:id="@+id/notification_icon_area"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:orientation="horizontal" />
+
+        </LinearLayout>
+
+        <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
+        <android.widget.Space
+            android:id="@+id/cutout_space_view"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:gravity="center_horizontal|center_vertical"
         />
 
-        <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
-             PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
-        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-            android:id="@+id/notification_icon_area"
-            android:layout_width="0dip"
+        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
+            android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:orientation="horizontal" />
-
-        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
             android:orientation="horizontal"
+            android:gravity="center_vertical|end"
             >
 
             <include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c5e5ee1..bef0830 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,6 @@
             android:layout_width="@dimen/qs_panel_width"
             android:layout_height="match_parent"
             android:layout_gravity="@integer/notification_panel_layout_gravity"
-            android:layout_marginStart="@dimen/notification_side_paddings"
-            android:layout_marginEnd="@dimen/notification_side_paddings"
             android:clipToPadding="false"
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bfa92ad..1fafb2f 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -16,12 +16,13 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/system_icons"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_vertical">
 
-    <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
+    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
+        android:layout_width="0dp"
+        android:layout_weight="1"
         android:layout_height="match_parent"
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values-h650dp/dimens.xml b/packages/SystemUI/res/values-h650dp/dimens.xml
index fbfca46..3811f67 100644
--- a/packages/SystemUI/res/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res/values-h650dp/dimens.xml
@@ -15,8 +15,7 @@
   -->
 
 <resources>
-    <dimen name="keyguard_clock_notifications_margin_min">32dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">32dp</dimen>
 
     <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">18.5%</fraction>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 49a7a29..a5e37d5 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -19,8 +19,7 @@
     <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
 
-    <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">36dp</dimen>
 
     <dimen name="keyguard_indication_margin_bottom">80dp</dimen>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c8ffe8f..268fdec 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -46,8 +46,7 @@
 
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin_min">44dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">44dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">44dp</dimen>
 
     <!-- Height of the status bar header bar when on Keyguard -->
     <dimen name="status_bar_header_height_keyguard">60dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0134086..887d3cb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,7 +215,7 @@
     <dimen name="close_handle_underlap">32dp</dimen>
 
     <!-- Height of the status bar header bar -->
-    <dimen name="status_bar_header_height">124dp</dimen>
+    <dimen name="status_bar_header_height">178dp</dimen>
 
     <!-- Height of the status bar header bar in the car setting. -->
     <dimen name="car_status_bar_header_height">128dp</dimen>
@@ -223,8 +223,9 @@
     <!-- The bottom padding of the status bar header. -->
     <dimen name="status_bar_header_padding_bottom">48dp</dimen>
 
-    <!-- The height of the container that holds the system icons in the quick settings header. -->
-    <dimen name="qs_header_system_icons_area_height">40dp</dimen>
+    <!-- The height of the container that holds the battery and time in the quick settings header.
+         -->
+    <dimen name="qs_header_system_icons_area_height">48dp</dimen>
 
     <!-- The height of the container that holds the system icons in the quick settings header in the
          car setting. -->
@@ -418,13 +419,12 @@
     <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
          max value is used when no notifications are displaying, and the min value is when the
          highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+    <fraction name="keyguard_clock_y_fraction_max">45%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
 
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin_min">30dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">42dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
     <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
@@ -863,7 +863,7 @@
     <dimen name="burn_in_prevention_offset_y">50dp</dimen>
 
     <!-- padding between the notification stack and the keyguard status view when dozing -->
-    <dimen name="dozing_stack_padding">10dp</dimen>
+    <dimen name="dozing_stack_padding">30dp</dimen>
 
     <dimen name="corner_size">16dp</dimen>
     <dimen name="top_padding">0dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fed97c5..edda613 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -93,5 +93,8 @@
     <item type="id" name="action_snooze_long"/>
     <item type="id" name="action_snooze_longer"/>
     <item type="id" name="action_snooze_assistant_suggestion_1"/>
+
+    <!-- For StatusBarIconContainer to tag its icon views -->
+    <item type="id" name="status_bar_view_state_tag" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 199ccfc..ab83bcf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -228,6 +228,8 @@
     <string name="accessibility_menu">Menu</string>
     <!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_accessibility_button">Accessibility</string>
+    <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_rotate_button">Rotate screen</string>
     <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_recent">Overview</string>
     <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4837fef..bcce6d1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -173,7 +173,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">#ffffffff</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 13f30b2..7d159b7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorSet;
 import android.animation.RectEvaluator;
 import android.annotation.FloatRange;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -39,6 +40,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.ViewRootImpl;
 import android.view.ViewStub;
 
 import java.util.ArrayList;
@@ -294,17 +296,25 @@
     }
 
     /**
-     * @return The next frame name for the specified surface.
+     * @return The next frame name for the specified surface or -1 if the surface is no longer
+     *         valid.
      */
     public static long getNextFrameNumber(Surface s) {
-        return s.getNextFrameNumber();
+        return s != null && s.isValid()
+                ? s.getNextFrameNumber()
+                : -1;
+
     }
 
     /**
      * @return The surface for the specified view.
      */
-    public static Surface getSurface(View v) {
-        return v.getViewRootImpl().mSurface;
+    public static @Nullable Surface getSurface(View v) {
+        ViewRootImpl viewRoot = v.getViewRootImpl();
+        if (viewRoot == null) {
+            return null;
+        }
+        return viewRoot.mSurface;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2fe66a1..8666b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -224,7 +224,6 @@
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
                 updatePercentText();
                 addView(mBatteryPercentView,
-                        0,
                         new ViewGroup.LayoutParams(
                                 LayoutParams.WRAP_CONTENT,
                                 LayoutParams.MATCH_PARENT));
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@
         public void onScanningStateChanged(boolean started) { }
         @Override
         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+        @Override
+        public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                          int bluetoothProfile) { }
     }
 
     private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..c0fed34 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -42,9 +42,8 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
-
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -63,10 +62,6 @@
     // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
     private static final boolean ENABLE_FLING_DISMISS = false;
 
-    // These values are used for metrics and should never change
-    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
-    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
-
     private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
 
     // Allow dragging the PIP to a location to close it
@@ -163,8 +158,7 @@
         @Override
         public void onPipDismiss() {
             mMotionHelper.dismissPip();
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                    METRIC_VALUE_DISMISSED_BY_TAP);
+            MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext);
         }
 
         @Override
@@ -463,8 +457,7 @@
             return;
         }
         if (mIsMinimized != isMinimized) {
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
-                    isMinimized);
+            MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, isMinimized);
         }
         mIsMinimized = isMinimized;
         mSnapAlgorithm.setMinimized(isMinimized);
@@ -537,8 +530,7 @@
         mMenuState = menuState;
         updateMovementBounds(menuState);
         if (menuState != MENU_STATE_CLOSE) {
-            MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
-                    menuState == MENU_STATE_FULL);
+            MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
         }
     }
 
@@ -670,9 +662,7 @@
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
                         vel.y, mUpdateScrimListener);
-                    MetricsLogger.action(mContext,
-                            MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                            METRIC_VALUE_DISMISSED_BY_DRAG);
+                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext);
                     return true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7f0acc2..7320b86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -45,8 +45,9 @@
     protected float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private View mQSFooter;
-    private float mFullElevation;
+    private View mBackground;
     private float mRadius;
+    private int mSideMargins;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -60,12 +61,14 @@
         mHeader = findViewById(R.id.header);
         mQSCustomizer = findViewById(R.id.qs_customize);
         mQSFooter = findViewById(R.id.qs_footer);
-        mFullElevation = mQSPanel.getElevation();
+        mBackground = findViewById(R.id.quick_settings_background);
         mRadius = getResources().getDimensionPixelSize(
                 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         setClickable(true);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+        setMargins();
     }
 
     @Override
@@ -131,6 +134,8 @@
         mQSDetail.setBottom(getTop() + height);
         // Pin QS Footer to the bottom of the panel.
         mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+        mBackground.setTop(mQSPanel.getTop());
+        mBackground.setBottom(height);
 
         ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
                 mRadius,
@@ -148,4 +153,19 @@
         mQsExpansion = expansion;
         updateExpansion();
     }
+
+    private void setMargins() {
+        setMargins(mQSDetail);
+        setMargins(mBackground);
+        setMargins(mQSFooter);
+        setMargins(mQSPanel);
+        setMargins(mHeader);
+        setMargins(mQSCustomizer);
+    }
+
+    private void setMargins(View view) {
+        FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        lp.rightMargin = mSideMargins;
+        lp.leftMargin = mSideMargins;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 927a49c..92475da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -61,18 +61,16 @@
 public class QSFooterImpl extends FrameLayout implements QSFooter,
         OnClickListener, OnUserInfoChangedListener, EmergencyListener,
         SignalCallback, CommandQueue.Callbacks {
-    private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
-
     private ActivityStarter mActivityStarter;
     private UserInfoController mUserInfoController;
     private SettingsButton mSettingsButton;
     protected View mSettingsContainer;
+    private View mCarrierText;
 
     private boolean mQsDisabled;
     private QSPanel mQsPanel;
 
     private boolean mExpanded;
-    protected ExpandableIndicator mExpandIndicator;
 
     private boolean mListening;
 
@@ -100,18 +98,18 @@
                 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
                         mQsPanel.showEdit(view)));
 
-        mExpandIndicator = findViewById(R.id.expand_indicator);
         mSettingsButton = findViewById(R.id.settings_button);
         mSettingsContainer = findViewById(R.id.settings_button_container);
         mSettingsButton.setOnClickListener(this);
 
+        mCarrierText = findViewById(R.id.qs_carrier_text);
+
         mMultiUserSwitch = findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
         ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
 
         updateResources();
 
@@ -162,6 +160,8 @@
         return new TouchAnimator.Builder()
                 .addFloat(mEdit, "alpha", 0, 1)
                 .addFloat(mMultiUserSwitch, "alpha", 0, 1)
+                .addFloat(mCarrierText, "alpha", 0, 1)
+                .addFloat(mSettingsButton, "alpha", 0, 1)
                 .build();
     }
 
@@ -185,8 +185,6 @@
         if (mSettingsAlpha != null) {
             mSettingsAlpha.setPosition(headerExpansionFraction);
         }
-
-        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
     }
 
     @Override
@@ -237,8 +235,6 @@
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
 
-        mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-
         final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 398592a..a97b35c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -18,10 +18,12 @@
 import static android.app.StatusBarManager.DISABLE_NONE;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.provider.AlarmClock;
 import android.support.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.view.View;
@@ -38,9 +40,11 @@
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
-public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
+public class QuickStatusBarHeader extends RelativeLayout
+        implements CommandQueue.Callbacks, View.OnClickListener {
 
     private ActivityStarter mActivityStarter;
 
@@ -53,6 +57,8 @@
     protected QuickQSPanel mHeaderQsPanel;
     protected QSTileHost mHost;
 
+    private View mDate;
+
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,21 +69,21 @@
         Resources res = getResources();
 
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
+        mDate = findViewById(R.id.date);
+        mDate.setOnClickListener(this);
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
 
         updateResources();
 
-        // Set the light/dark theming on the header status UI to match the current theme.
+        // Set light text on the header icons because they will always be on a black background
         int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
-        float intensity = colorForeground == Color.WHITE ? 0 : 1;
         Rect tintArea = new Rect(0, 0, 0, 0);
-
-        applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
-        applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+        applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
 
         BatteryMeterView battery = findViewById(R.id.battery);
+        battery.setFillColor(Color.WHITE);
         battery.setForceShowPercent(true);
 
         mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -146,6 +152,14 @@
         super.onDetachedFromWindow();
     }
 
+    @Override
+    public void onClick(View v) {
+        if (v == mDate) {
+            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+                    AlarmClock.ACTION_SHOW_ALARMS), 0);
+        }
+    }
+
     public void setListening(boolean listening) {
         if (listening == mListening) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9e265e22..4ceace3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -33,7 +33,6 @@
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.quicksettings.Tile;
-import android.util.Log;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -55,7 +54,6 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
 import com.android.systemui.volume.ZenModePanel;
 
 /** Quick settings tile: Do not disturb **/
@@ -134,8 +132,7 @@
         if (mState.value) {
             mController.setZen(ZEN_MODE_OFF, null, TAG);
         } else {
-            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
-            mController.setZen(zen, null, TAG);
+            mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
         }
     }
 
@@ -159,9 +156,7 @@
                     showDetail(true);
                 }
             });
-            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
-                    Global.ZEN_MODE_ALARMS);
-            mController.setZen(zen, null, TAG);
+            mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
         } else {
             showDetail(true);
         }
@@ -313,9 +308,7 @@
                 mController.setZen(ZEN_MODE_OFF, null, TAG);
                 mAuto = false;
             } else {
-                int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
-                        ZEN_MODE_ALARMS);
-                mController.setZen(zen, null, TAG);
+                mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 6db46b5..675aa8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,12 +293,13 @@
             sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
 
-            // Create a share action for the notification. Note, we proxy the call to ShareReceiver
-            // because RemoteViews currently forces an activity options on the PendingIntent being
-            // launched, and since we don't want to trigger the share sheet in this case, we will
-            // start the chooser activitiy directly in ShareReceiver.
+            // Create a share action for the notification. Note, we proxy the call to
+            // ScreenshotActionReceiver because RemoteViews currently forces an activity options
+            // on the PendingIntent being launched, and since we don't want to trigger the share
+            // sheet in this case, we start the chooser activity directly in
+            // ScreenshotActionReceiver.
             PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
-                    new Intent(context, GlobalScreenshot.ShareReceiver.class)
+                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
                             .putExtra(SHARING_INTENT, sharingIntent),
                     PendingIntent.FLAG_CANCEL_CURRENT);
             Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -306,15 +307,19 @@
                     r.getString(com.android.internal.R.string.share), shareAction);
             mNotificationBuilder.addAction(shareActionBuilder.build());
 
-            // Create a delete action for the notification
-            PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
-                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
-                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
-                    R.drawable.ic_screenshot_delete,
-                    r.getString(com.android.internal.R.string.delete), deleteAction);
-            mNotificationBuilder.addAction(deleteActionBuilder.build());
+            Intent editIntent = new Intent(Intent.ACTION_EDIT);
+            editIntent.setType("image/png");
+            editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+
+            // Create a edit action for the notification the same way.
+            PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
+                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
+                            .putExtra(SHARING_INTENT, editIntent),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+                    R.drawable.ic_screenshot_edit,
+                    r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+            mNotificationBuilder.addAction(editActionBuilder.build());
 
             mParams.imageUri = uri;
             mParams.image = null;
@@ -879,9 +884,9 @@
     }
 
     /**
-     * Receiver to proxy the share intent.
+     * Receiver to proxy the share or edit intent.
      */
-    public static class ShareReceiver extends BroadcastReceiver {
+    public static class ScreenshotActionReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             try {
@@ -903,7 +908,7 @@
     }
 
     /**
-     * Removes the notification for a screenshot after a share target is chosen.
+     * Removes the notification for a screenshot after a share or edit target is chosen.
      */
     public static class TargetChosenReceiver extends BroadcastReceiver {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 9b123cb..7284ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -34,6 +34,7 @@
     private View.OnClickListener mClickListener;
     private View.OnTouchListener mTouchListener;
     private View.OnLongClickListener mLongClickListener;
+    private View.OnHoverListener mOnHoverListener;
     private Boolean mLongClickable;
     private Integer mAlpha;
     private Float mDarkIntensity;
@@ -56,6 +57,7 @@
         view.setOnClickListener(mClickListener);
         view.setOnTouchListener(mTouchListener);
         view.setOnLongClickListener(mLongClickListener);
+        view.setOnHoverListener(mOnHoverListener);
         if (mLongClickable != null) {
             view.setLongClickable(mLongClickable);
         }
@@ -174,6 +176,14 @@
         }
     }
 
+    public void setOnHoverListener(View.OnHoverListener hoverListener) {
+        mOnHoverListener = hoverListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnHoverListener(mOnHoverListener);
+        }
+    }
+
     public void setClickable(boolean clickable) {
         abortCurrentGesture();
         final int N = mViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index f7aa818..389be1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -20,6 +20,7 @@
 
 import android.content.res.Resources;
 import android.graphics.Path;
+import android.util.MathUtils;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.PathInterpolator;
 
@@ -45,8 +46,7 @@
     private static final float BURN_IN_PREVENTION_PERIOD_Y = 521;
     private static final float BURN_IN_PREVENTION_PERIOD_X = 83;
 
-    private int mClockNotificationsMarginMin;
-    private int mClockNotificationsMarginMax;
+    private int mClockNotificationsMargin;
     private float mClockYFractionMin;
     private float mClockYFractionMax;
     private int mMaxKeyguardNotifications;
@@ -84,10 +84,8 @@
      * Refreshes the dimension values.
      */
     public void loadDimens(Resources res) {
-        mClockNotificationsMarginMin = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_min);
-        mClockNotificationsMarginMax = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_max);
+        mClockNotificationsMargin = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin);
         mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
         mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
         mMoreCardNotificationAmount =
@@ -117,7 +115,7 @@
 
     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
         return mClockYFractionMin * height + keyguardStatusHeight / 2
-                + mClockNotificationsMarginMin;
+                + mClockNotificationsMargin;
     }
 
     public void run(Result result) {
@@ -125,21 +123,15 @@
         float clockAdjustment = getClockYExpansionAdjustment();
         float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
         result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
-        int clockNotificationsPadding = getClockNotificationsPadding()
+        result.clockY = y;
+        int clockNotificationsPadding = mClockNotificationsMargin
                 + result.stackScrollerPaddingAdjustment;
         int padding = y + clockNotificationsPadding;
-        result.clockY = y;
-        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
-        result.clockScale = getClockScale(result.stackScrollerPadding,
-                result.clockY,
-                y + getClockNotificationsPadding() + mKeyguardStatusHeight);
+        result.clockScale = getClockScale(mKeyguardStatusHeight + padding,
+                y, y + mClockNotificationsMargin + mKeyguardStatusHeight);
         result.clockAlpha = getClockAlpha(result.clockScale);
 
-        result.stackScrollerPadding = (int) interpolate(
-                result.stackScrollerPadding,
-                mClockBottom + y + mDozingStackPadding,
-                mDarkAmount);
-
+        result.stackScrollerPadding = mClockBottom + y + mDozingStackPadding;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -154,22 +146,16 @@
         return interpolate(progress, 1, mDarkAmount);
     }
 
-    private int getClockNotificationsPadding() {
-        float t = getNotificationAmountT();
-        t = Math.min(t, 1.0f);
-        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
-    }
-
     private float getClockYFraction() {
         float t = getNotificationAmountT();
         t = Math.min(t, 1.0f);
-        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+        return MathUtils.lerp(mClockYFractionMax, mClockYFractionMin, t);
     }
 
     private int getClockY() {
-        // Dark: Align the bottom edge of the clock at one third:
-        // clockBottomEdge = result - mKeyguardStatusHeight / 2 + mClockBottom
-        float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom)
+        // Dark: Align the bottom edge of the clock at about half of the screen:
+        float clockYDark = (mClockYFractionMax * mHeight +
+                (float) mKeyguardStatusHeight / 2 - mClockBottom)
                 + burnInPreventionOffsetY();
         float clockYRegular = getClockYFraction() * mHeight;
         return (int) interpolate(clockYRegular, clockYDark, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 4225843..368b36b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -26,7 +26,6 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -111,7 +110,7 @@
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
-    private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000;
+    private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
 
     protected NavigationBarView mNavigationBarView = null;
     protected AssistManager mAssistManager;
@@ -120,6 +119,7 @@
 
     private int mNavigationIconHints = 0;
     private int mNavigationBarMode;
+    private boolean mAccessibilityFeedbackEnabled;
     private AccessibilityManager mAccessibilityManager;
     private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
@@ -143,6 +143,7 @@
     public boolean mHomeBlockedThisTouch;
 
     private int mLastRotationSuggestion;
+    private boolean mHoveringRotationSuggestion;
     private RotationLockController mRotationLockController;
     private TaskStackListenerImpl mTaskStackListener;
 
@@ -345,40 +346,67 @@
             return;
         }
 
-        Handler h = getView().getHandler();
         if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
             // Use this as a signal to remove any current suggestions
-            h.removeCallbacks(mRemoveRotationProposal);
+            getView().getHandler().removeCallbacks(mRemoveRotationProposal);
             setRotateSuggestionButtonState(false);
         } else {
             mLastRotationSuggestion = rotation; // Remember rotation for click
             setRotateSuggestionButtonState(true);
-            h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
-            h.postDelayed(mRemoveRotationProposal,
-                    ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout
+            rescheduleRotationTimeout(false);
         }
     }
 
+    private void rescheduleRotationTimeout(final boolean reasonHover) {
+        // May be called due to a new rotation proposal or a change in hover state
+        if (reasonHover) {
+            // Don't reschedule if a hide animator is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                return;
+            }
+            // Don't reschedule if not visible
+            if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) {
+                return;
+            }
+        }
+
+        Handler h = getView().getHandler();
+        h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
+        h.postDelayed(mRemoveRotationProposal,
+                computeRotationProposalTimeout()); // Schedule timeout
+    }
+
+    private int computeRotationProposalTimeout() {
+        if (mAccessibilityFeedbackEnabled) return 20000;
+        if (mHoveringRotationSuggestion) return 16000;
+        return 6000;
+    }
+
     public void setRotateSuggestionButtonState(final boolean visible) {
         setRotateSuggestionButtonState(visible, false);
     }
 
     public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
         ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
-        boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
+        final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
 
         // Rerun a show animation to indicate change but don't rerun a hide animation
         if (!visible && !currentlyVisible) return;
 
-        View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
+        View currentView = rotBtn.getCurrentView();
         if (currentView == null) return;
 
-        KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable();
+        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
         if (kbd == null) return;
 
-        AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        AnimatedVectorDrawable animIcon = null;
+        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        }
+
         if (visible) { // Appear and change
             rotBtn.setVisibility(View.VISIBLE);
+            mNavigationBarView.notifyAccessibilitySubtreeChanged();
 
             if (skipAnim) {
                 currentView.setAlpha(1f);
@@ -391,18 +419,22 @@
 
             ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
                     0f, 1f);
-            appearFade.setDuration(100);
+            appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
             appearFade.setInterpolator(Interpolators.LINEAR);
             mRotateShowAnimator = appearFade;
             appearFade.start();
 
-            // Run the rotate icon's animation
-            animIcon.reset();
-            animIcon.start();
+            // Run the rotate icon's animation if it has one
+            if (animIcon != null) {
+                animIcon.reset();
+                animIcon.start();
+            }
+
         } else { // Hide
 
             if (skipAnim) {
                 rotBtn.setVisibility(View.INVISIBLE);
+                mNavigationBarView.notifyAccessibilitySubtreeChanged();
                 return;
             }
 
@@ -413,12 +445,13 @@
 
             ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
                     0f);
-            fadeOut.setDuration(100);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
             fadeOut.setInterpolator(Interpolators.LINEAR);
             fadeOut.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     rotBtn.setVisibility(View.INVISIBLE);
+                    mNavigationBarView.notifyAccessibilitySubtreeChanged();
                 }
             });
 
@@ -532,6 +565,7 @@
 
         ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
         rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
+        rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
     }
 
     private boolean onHomeTouch(View v, MotionEvent event) {
@@ -707,6 +741,7 @@
         } catch (Settings.SettingNotFoundException e) {
         }
 
+        boolean feedbackEnabled = false;
         // AccessibilityManagerService resolves services for the current user since the local
         // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
         final List<AccessibilityServiceInfo> services =
@@ -717,8 +752,15 @@
             if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
                 requestingServices++;
             }
+
+            if (info.feedbackType != 0 && info.feedbackType !=
+                    AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+                feedbackEnabled = true;
+            }
         }
 
+        mAccessibilityFeedbackEnabled = feedbackEnabled;
+
         final boolean showAccessibilityButton = requestingServices >= 1;
         final boolean targetSelection = requestingServices >= 2;
         mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
@@ -728,6 +770,14 @@
         mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
     }
 
+    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
+        final int action = event.getActionMasked();
+        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
+                || (action == MotionEvent.ACTION_HOVER_MOVE);
+        rescheduleRotationTimeout(true);
+        return false; // Must return false so a11y hover events are dispatched correctly.
+    }
+
     // ----- Methods that StatusBar talks to (should be minimized) -----
 
     public void setLightBarController(LightBarController lightBarController) {
@@ -775,18 +825,18 @@
 
     private final Stub mRotationWatcher = new Stub() {
         @Override
-        public void onRotationChanged(int rotation) throws RemoteException {
-            // If the screen rotation changes while locked, update lock rotation to flow with
-            // new screen rotation and hide any showing suggestions.
-            if (mRotationLockController.isRotationLocked()) {
-                mRotationLockController.setRotationLockedAtAngle(true, rotation);
-                setRotateSuggestionButtonState(false, true);
-            }
-
+        public void onRotationChanged(final int rotation) throws RemoteException {
             // We need this to be scheduled as early as possible to beat the redrawing of
             // window in response to the orientation change.
             Handler h = getView().getHandler();
             Message msg = Message.obtain(h, () -> {
+                // If the screen rotation changes while locked, update lock rotation to flow with
+                // new screen rotation and hide any showing suggestions.
+                if (mRotationLockController.isRotationLocked()) {
+                    mRotationLockController.setRotationLockedAtAngle(true, rotation);
+                    setRotateSuggestionButtonState(false, true);
+                }
+
                 if (mNavigationBarView != null
                         && mNavigationBarView.needsReorient(rotation)) {
                     repositionNavigationBar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 970d1de..b181212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,26 +16,34 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.EventLog;
+import android.view.DisplayCutout;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DejankUtils;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.util.leak.RotationUtils;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
     private static final boolean DEBUG = StatusBar.DEBUG;
     private static final boolean DEBUG_GESTURES = false;
+    private static final int NO_VALUE = Integer.MIN_VALUE;
 
     StatusBar mBar;
 
@@ -53,6 +61,11 @@
         }
     };
     private DarkReceiver mBattery;
+    private int mLastOrientation;
+    @Nullable
+    private View mCutoutSpace;
+    @Nullable
+    private DisplayCutout mDisplayCutout;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -76,6 +89,7 @@
     public void onFinishInflate() {
         mBarTransitions.init();
         mBattery = findViewById(R.id.battery);
+        mCutoutSpace = findViewById(R.id.cutout_space_view);
     }
 
     @Override
@@ -83,12 +97,51 @@
         super.onAttachedToWindow();
         // Always have Battery meters in the status bar observe the dark/light modes.
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+        if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+            postUpdateLayoutForCutout();
+        }
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+        mDisplayCutout = null;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // May trigger cutout space layout-ing
+        if (updateOrientationAndCutout(newConfig.orientation)) {
+            postUpdateLayoutForCutout();
+        }
+    }
+
+    /**
+     *
+     * @param newOrientation may pass NO_VALUE for no change
+     * @return boolean indicating if we need to update the cutout location / margins
+     */
+    private boolean updateOrientationAndCutout(int newOrientation) {
+        boolean changed = false;
+        if (newOrientation != NO_VALUE) {
+            if (mLastOrientation != newOrientation) {
+                changed = true;
+                mLastOrientation = newOrientation;
+            }
+        }
+
+        if (mDisplayCutout == null) {
+            DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+            if (cutout != null) {
+                changed = true;
+                mDisplayCutout = cutout;
+            }
+        }
+
+        return changed;
     }
 
     @Override
@@ -214,4 +267,80 @@
                 R.dimen.status_bar_height);
         setLayoutParams(layoutParams);
     }
+
+    private void updateLayoutForCutout() {
+        updateCutoutLocation();
+        updateSafeInsets();
+    }
+
+    private void postUpdateLayoutForCutout() {
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                updateLayoutForCutout();
+            }
+        };
+        // Let the cutout emulation draw first
+        postDelayed(r, 0);
+    }
+
+    private void updateCutoutLocation() {
+        // Not all layouts have a cutout (e.g., Car)
+        if (mCutoutSpace == null) {
+            return;
+        }
+
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+                    || mLastOrientation != ORIENTATION_PORTRAIT) {
+            mCutoutSpace.setVisibility(View.GONE);
+            return;
+        }
+
+        mCutoutSpace.setVisibility(View.VISIBLE);
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
+        lp.width = mDisplayCutout.getBoundingRect().width();
+        lp.height = mDisplayCutout.getBoundingRect().height();
+    }
+
+    private void updateSafeInsets() {
+        // Depending on our rotation, we may have to work around a cutout in the middle of the view,
+        // or letterboxing from the right or left sides.
+
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+            lp.leftMargin = 0;
+            lp.rightMargin = 0;
+            return;
+        }
+
+        int leftMargin = 0;
+        int rightMargin = 0;
+        switch (RotationUtils.getRotation(getContext())) {
+            /*
+             * Landscape: <-|
+             * Seascape:  |->
+             */
+            case RotationUtils.ROTATION_LANDSCAPE:
+                leftMargin = getDisplayCutoutHeight();
+                break;
+            case RotationUtils.ROTATION_SEASCAPE:
+                rightMargin = getDisplayCutoutHeight();
+                break;
+            default:
+                break;
+        }
+
+        lp.leftMargin = leftMargin;
+        lp.rightMargin = rightMargin;
+    }
+
+    //TODO: Find a better way
+    private int getDisplayCutoutHeight() {
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+            return 0;
+        }
+
+        Rect r = mDisplayCutout.getBoundingRect();
+        return r.bottom - r.top;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 6220fcb..1130b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -32,6 +32,8 @@
 
 public class SettingsButton extends AlphaOptimizedImageButton {
 
+    private static final boolean TUNER_ENABLE_AVAILABLE = false;
+
     private static final long LONG_PRESS_LENGTH = 1000;
     private static final long ACCEL_LENGTH = 750;
     private static final long FULL_SPEED_LENGTH = 375;
@@ -59,7 +61,7 @@
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
+                if (TUNER_ENABLE_AVAILABLE) postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
                 break;
             case MotionEvent.ACTION_UP:
                 if (mUpToSpeed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index bcda60e..07610ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -62,7 +62,7 @@
     }
 
     /**
-     * Version of ViewGroup that observers state from the DarkIconDispatcher.
+     * Version of ViewGroup that observes state from the DarkIconDispatcher.
      */
     public static class DarkIconManager extends IconManager {
         private final DarkIconDispatcher mDarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
new file mode 100644
index 0000000..1897171
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import android.view.View;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.stack.ViewState;
+
+public class StatusIconContainer extends AlphaOptimizedLinearLayout {
+
+    private static final String TAG = "StatusIconContainer";
+    private static final int MAX_ICONS = 5;
+    private static final int MAX_DOTS = 3;
+
+    public StatusIconContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        float midY = getHeight() / 2.0f;
+
+        // Layout all child views so that we can move them around later
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            int width = child.getMeasuredWidth();
+            int height = child.getMeasuredHeight();
+            int top = (int) (midY - height / 2.0f);
+            child.layout(0, top, width, top + height);
+        }
+
+        resetViewStates();
+        calculateIconTranslations();
+        applyIconStates();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int count = getChildCount();
+        // Measure all children so that they report the correct width
+        for (int i = 0; i < count; i++) {
+            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        ViewState vs = new ViewState();
+        child.setTag(R.id.status_bar_view_state_tag, vs);
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        child.setTag(R.id.status_bar_view_state_tag, null);
+    }
+
+    /**
+     * Layout is happening from end -> start
+     */
+    private void calculateIconTranslations() {
+        float translationX = getWidth();
+        float contentStart = getPaddingStart();
+        int childCount = getChildCount();
+        // Underflow === don't show content until that index
+        int firstUnderflowIndex = -1;
+        android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+
+        //TODO: Dots
+        for (int i = childCount - 1; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (!(child instanceof StatusBarIconView)) {
+                continue;
+            }
+
+            ViewState childState = getViewStateFromChild(child);
+            if (childState == null ) {
+                continue;
+            }
+
+            // Rely on StatusBarIcon for truth about visibility
+            if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+                childState.hidden = true;
+                continue;
+            }
+
+            childState.xTranslation = translationX - child.getWidth();
+
+            if (childState.xTranslation < contentStart) {
+                if (firstUnderflowIndex == -1) {
+                    firstUnderflowIndex = i;
+                }
+            }
+
+            translationX -= child.getWidth();
+        }
+
+        if (firstUnderflowIndex != -1) {
+            for (int i = 0; i <= firstUnderflowIndex; i++) {
+                View child = getChildAt(i);
+                ViewState vs = getViewStateFromChild(child);
+                if (vs != null) {
+                    vs.hidden = true;
+                }
+            }
+        }
+    }
+
+    private void applyIconStates() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            ViewState vs = getViewStateFromChild(child);
+            if (vs != null) {
+                vs.applyToView(child);
+            }
+        }
+    }
+
+    private void resetViewStates() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            ViewState vs = getViewStateFromChild(child);
+            if (vs == null) {
+                continue;
+            }
+
+            vs.initFrom(child);
+            vs.alpha = 1.0f;
+            if (child instanceof StatusBarIconView) {
+                vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+            } else {
+                vs.hidden = false;
+            }
+        }
+    }
+
+    private static @Nullable ViewState getViewStateFromChild(View child) {
+        return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
     private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
         ActuallyCachedState state = mCachedState.get(device);
         if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 167508a..443e760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -29,6 +29,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PointF;
@@ -43,6 +44,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Pair;
 import android.util.Property;
 import android.view.ContextThemeWrapper;
@@ -123,7 +125,7 @@
     /**
      * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
      */
-    private static final int INVALID_POINTER = -1;;
+    private static final int INVALID_POINTER = -1;
 
     private ExpandHelper mExpandHelper;
     private NotificationSwipeHelper mSwipeHelper;
@@ -158,7 +160,12 @@
     private int mCollapsedSize;
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
+    private int mRegularTopPadding;
+    private int mDarkTopPadding;
+    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
     private int mTopPadding;
+    // Distance between AOD separator and shelf
+    private int mDarkSeparatorPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
 
@@ -356,17 +363,17 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
-    private float mBackgroundFadeAmount = 1.0f;
-    private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
-            new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
+    private float mDarkAmount = 1.0f;
+    private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
+            new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
                 @Override
                 public void setValue(NotificationStackScrollLayout object, float value) {
-                    object.setBackgroundFadeAmount(value);
+                    object.setDarkAmount(value);
                 }
 
                 @Override
                 public Float get(NotificationStackScrollLayout object) {
-                    return object.getBackgroundFadeAmount();
+                    return object.getDarkAmount();
                 }
             };
     private boolean mUsingLightTheme;
@@ -389,6 +396,9 @@
     private Runnable mAnimateScroll = this::animateScroll;
     private int mCornerRadius;
     private int mSidePaddings;
+    private final int mSeparatorWidth;
+    private final int mSeparatorThickness;
+    private final Rect mTmpRect = new Rect();
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -423,9 +433,12 @@
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         mFadeNotificationsOnDismiss =
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
+        mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
+        mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
+        mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
 
         updateWillNotDraw();
-        mBackgroundPaint.setAntiAlias(true);;
+        mBackgroundPaint.setAntiAlias(true);
         if (DEBUG) {
             mDebugPaint = new Paint();
             mDebugPaint.setColor(0xffff0000);
@@ -472,10 +485,9 @@
     }
 
     protected void onDraw(Canvas canvas) {
-        if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
-                && mCurrentBounds.top < mCurrentBounds.bottom) {
-            canvas.drawRoundRect(mSidePaddings, mCurrentBounds.top, getWidth() - mSidePaddings,
-                    mCurrentBounds.bottom, mCornerRadius, mCornerRadius, mBackgroundPaint);
+        if (mShouldDrawNotificationBackground
+                && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
+            drawBackground(canvas);
         }
 
         if (DEBUG) {
@@ -488,17 +500,55 @@
         }
     }
 
+    private void drawBackground(Canvas canvas) {
+        final int lockScreenLeft = mSidePaddings;
+        final int lockScreenRight = getWidth() - mSidePaddings;
+        final int lockScreenTop = mCurrentBounds.top;
+        final int lockScreenBottom = mCurrentBounds.bottom;
+        final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
+        final int darkRight = darkLeft + mSeparatorWidth;
+        final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
+        final int darkBottom = darkTop + mSeparatorThickness;
+
+        if (mAmbientState.isDark()) {
+            // Only draw divider on AOD if we actually have notifications
+            if (mFirstVisibleBackgroundChild != null) {
+                canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
+            }
+            setClipBounds(null);
+        } else {
+            float animProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(mDarkAmount);
+            float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(mDarkAmount * 2);
+            mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
+                    (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
+                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
+            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+            setClipBounds(animProgress == 1 ? null : mTmpRect);
+        }
+    }
+
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground) {
             return;
         }
 
-        float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
-        alpha *= mBackgroundFadeAmount;
-        // We need to manually blend in the background color
-        int scrimColor = mScrimController.getBackgroundColor();
-        int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+        final int color;
+        if (mAmbientState.isDark()) {
+            color = Color.WHITE;
+        } else {
+            float alpha =
+                    BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+            alpha *= mDarkAmount;
+            // We need to manually blend in the background color
+            int scrimColor = mScrimController.getBackgroundColor();
+            color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+        }
+
         if (mCachedBackgroundColor != color) {
             mCachedBackgroundColor = color;
             mBackgroundPaint.setColor(color);
@@ -634,6 +684,7 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
+        mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -756,8 +807,9 @@
     }
 
     private void setTopPadding(int topPadding, boolean animate) {
-        if (mTopPadding != topPadding) {
-            mTopPadding = topPadding;
+        if (mRegularTopPadding != topPadding) {
+            mRegularTopPadding = topPadding;
+            mDarkTopPadding = topPadding + mDarkSeparatorPadding;
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -2262,7 +2314,7 @@
         }
 
         mScrimController.setExcludedBackgroundArea(
-                mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
+                mFadingOut || mParentNotFullyVisible || mDarkAmount != 1 || mIsClipped ? null
                         : mCurrentBounds);
         invalidate();
     }
@@ -3804,9 +3856,9 @@
             mDarkNeedsAnimation = true;
             mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
             mNeedsAnimation =  true;
-            setBackgroundFadeAmount(0.0f);
+            setDarkAmount(0.0f);
         } else if (!dark) {
-            setBackgroundFadeAmount(1.0f);
+            setDarkAmount(1.0f);
         }
         requestChildrenUpdate();
         if (dark) {
@@ -3826,21 +3878,21 @@
      * {@link #mAmbientState}'s dark mode is toggled.
      */
     private void updateWillNotDraw() {
-        boolean willDraw = !mAmbientState.isDark() && mShouldDrawNotificationBackground || DEBUG;
+        boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
         setWillNotDraw(!willDraw);
     }
 
-    private void setBackgroundFadeAmount(float fadeAmount) {
-        mBackgroundFadeAmount = fadeAmount;
+    private void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
         updateBackgroundDimming();
     }
 
-    public float getBackgroundFadeAmount() {
-        return mBackgroundFadeAmount;
+    public float getDarkAmount() {
+        return mDarkAmount;
     }
 
     private void startBackgroundFadeIn() {
-        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
+        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, 0f, 1f);
         fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
         fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
         fadeAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8e584bc..5a4478f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -58,7 +58,7 @@
 
     private static final String TUNER_VERSION = "sysui_tuner_version";
 
-    private static final int CURRENT_TUNER_VERSION = 1;
+    private static final int CURRENT_TUNER_VERSION = 2;
 
     private final Observer mObserver = new Observer();
     // Map of Uris we listen on to their settings keys.
@@ -116,6 +116,9 @@
                         TextUtils.join(",", iconBlacklist), mCurrentUser);
             }
         }
+        if (oldVersion < 2) {
+            setTunerEnabled(mContext, false);
+        }
         setValue(TUNER_VERSION, newVersion);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 66524cc..2edcd01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -74,6 +74,8 @@
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
     }
 
     @Test
@@ -87,11 +89,15 @@
 
         assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
 
         fragment.disable(0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
     }
 
     @Test
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
similarity index 65%
copy from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
copy to packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f4205ad6..4f3a8b1 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationNarrowOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
similarity index 64%
rename from packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
index dd43690..71ce6b4 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.internal.display.cutout.emulation"
-    android:versionCode="1"
-    android:versionName="1.0">
+        package="com.android.internal.display.cutout.emulation.narrow"
+        android:versionCode="1"
+        android:versionName="1.0">
     <overlay android:targetPackage="android" android:priority="1"/>
 
     <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
similarity index 95%
rename from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index 30e8b68..43bde88 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -41,7 +41,7 @@
     </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).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
similarity index 89%
rename from packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
index 5d5c425..4989677 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
@@ -18,7 +18,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Display Cutout Emulation</string>
+    <string name="display_cutout_emulation_overlay">Narrow display cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
similarity index 66%
copy from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
copy to packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f4205ad6..dac3878 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationTall
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationTallOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..5a93cfb
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.tall"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android" android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
similarity index 84%
copy from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
copy to packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 30e8b68..9cf48d9 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -30,18 +30,18 @@
          -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout">
         M 0,0
-        L -24, 0
-        L -21.9940446283, 20.0595537175
-        C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0
-        L 8.8, 32.0
-        C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175
-        L 24, 0
+        L -48, 0
+        L -44.3940446283, 36.0595537175
+        C -43.5582133885, 44.4178661152 -39.6, 48.0 -31.2, 48.0
+        L 31.2, 48.0
+        C 39.6, 48.0 43.5582133885, 44.4178661152 44.3940446283, 36.0595537175
+        L 48, 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).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
new file mode 100644
index 0000000..6dcbbd9
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/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">Tall display cutout</string>
+
+</resources>
+
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
similarity index 66%
rename from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
rename to packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index f4205ad6..f4f250c 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationWide
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWideOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..96bd060
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.wide"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android" android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
similarity index 84%
copy from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
copy to packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index 30e8b68..1ce41f0 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -30,18 +30,18 @@
          -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout">
         M 0,0
-        L -24, 0
-        L -21.9940446283, 20.0595537175
-        C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0
-        L 8.8, 32.0
-        C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175
-        L 24, 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).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
new file mode 100644
index 0000000..f4b9f7e
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/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">Wide display cutout</string>
+
+</resources>
+
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index cc3af8c..289dd14 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -114,14 +114,14 @@
     private RefactoredBackupManagerService backupManagerService;
     private final Object mCancelLock = new Object();
 
-    ArrayList<BackupRequest> mQueue;
-    ArrayList<BackupRequest> mOriginalQueue;
-    File mStateDir;
-    @Nullable DataChangedJournal mJournal;
-    BackupState mCurrentState;
-    List<String> mPendingFullBackups;
-    IBackupObserver mObserver;
-    IBackupManagerMonitor mMonitor;
+    private ArrayList<BackupRequest> mQueue;
+    private ArrayList<BackupRequest> mOriginalQueue;
+    private File mStateDir;
+    @Nullable private DataChangedJournal mJournal;
+    private BackupState mCurrentState;
+    private List<String> mPendingFullBackups;
+    private IBackupObserver mObserver;
+    private IBackupManagerMonitor mMonitor;
 
     private final TransportClient mTransportClient;
     private final OnTaskFinishedListener mListener;
@@ -130,18 +130,18 @@
     private volatile int mEphemeralOpToken;
 
     // carried information about the current in-flight operation
-    IBackupAgent mAgentBinder;
-    PackageInfo mCurrentPackage;
-    File mSavedStateName;
-    File mBackupDataName;
-    File mNewStateName;
-    ParcelFileDescriptor mSavedState;
-    ParcelFileDescriptor mBackupData;
-    ParcelFileDescriptor mNewState;
-    int mStatus;
-    boolean mFinished;
-    final boolean mUserInitiated;
-    final boolean mNonIncremental;
+    private IBackupAgent mAgentBinder;
+    private PackageInfo mCurrentPackage;
+    private File mSavedStateName;
+    private File mBackupDataName;
+    private File mNewStateName;
+    private ParcelFileDescriptor mSavedState;
+    private ParcelFileDescriptor mBackupData;
+    private ParcelFileDescriptor mNewState;
+    private int mStatus;
+    private boolean mFinished;
+    private final boolean mUserInitiated;
+    private final boolean mNonIncremental;
 
     private volatile boolean mCancelAll;
 
@@ -241,7 +241,7 @@
 
     // We're starting a backup pass.  Initialize the transport and send
     // the PM metadata blob if we haven't already.
-    void beginBackup() {
+    private void beginBackup() {
         if (DEBUG_BACKUP_TRACE) {
             backupManagerService.clearBackupTrace();
             StringBuilder b = new StringBuilder(256);
@@ -369,7 +369,7 @@
 
     // Transport has been initialized and the PM metadata submitted successfully
     // if that was warranted.  Now we process the single next thing in the queue.
-    void invokeNextAgent() {
+    private void invokeNextAgent() {
         mStatus = BackupTransport.TRANSPORT_OK;
         backupManagerService.addBackupTrace("invoke q=" + mQueue.size());
 
@@ -511,7 +511,7 @@
         }
     }
 
-    void finalizeBackup() {
+    private void finalizeBackup() {
         backupManagerService.addBackupTrace("finishing");
 
         // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
@@ -617,14 +617,14 @@
     }
 
     // Remove the PM metadata state. This will generate an init on the next pass.
-    void clearMetadata() {
+    private void clearMetadata() {
         final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
         if (pmState.exists()) pmState.delete();
     }
 
     // Invoke an agent's doBackup() and start a timeout message spinning on the main
     // handler in case it doesn't get back to us.
-    int invokeAgentForBackup(String packageName, IBackupAgent agent) {
+    private int invokeAgentForBackup(String packageName, IBackupAgent agent) {
         if (DEBUG) {
             Slog.d(TAG, "invokeAgentForBackup on " + packageName);
         }
@@ -711,7 +711,7 @@
         return BackupTransport.TRANSPORT_OK;
     }
 
-    public void failAgent(IBackupAgent agent, String message) {
+    private void failAgent(IBackupAgent agent, String message) {
         try {
             agent.fail(message);
         } catch (Exception e) {
@@ -1059,7 +1059,7 @@
         }
     }
 
-    void revertAndEndBackup() {
+    private void revertAndEndBackup() {
         if (MORE_DEBUG) {
             Slog.i(TAG, "Reverting backup queue - restaging everything");
         }
@@ -1085,14 +1085,14 @@
 
     }
 
-    void errorCleanup() {
+    private void errorCleanup() {
         mBackupDataName.delete();
         mNewStateName.delete();
         clearAgentState();
     }
 
     // Cleanup common to both success and failure cases
-    void clearAgentState() {
+    private void clearAgentState() {
         try {
             if (mSavedState != null) mSavedState.close();
         } catch (IOException e) {
@@ -1123,7 +1123,7 @@
         }
     }
 
-    void executeNextState(BackupState nextState) {
+    private void executeNextState(BackupState nextState) {
         if (MORE_DEBUG) {
             Slog.i(TAG, " => executing next step on "
                     + this + " nextState=" + nextState);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1f4b78..5030dce 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -69,6 +69,7 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
+import android.net.NetworkWatchlistManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -5708,6 +5709,17 @@
                 Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
     }
 
+    @Override
+    public byte[] getNetworkWatchlistConfigHash() {
+        NetworkWatchlistManager nwm = mContext.getSystemService(NetworkWatchlistManager.class);
+        if (nwm == null) {
+            loge("Unable to get NetworkWatchlistManager");
+            return null;
+        }
+        // Redirect it to network watchlist service to access watchlist file and calculate hash.
+        return nwm.getWatchlistConfigHash();
+    }
+
     @VisibleForTesting
     public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index a538bde..de113a6 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -26,6 +26,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -89,6 +91,9 @@
 
     private final MyHandler mHandler;
 
+    @VisibleForTesting
+    FeatureFlagsObserver mFlagsObserver;
+
     /**
      * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
      */
@@ -113,14 +118,36 @@
     @GuardedBy("mLock")
     boolean mStarted;
 
+    /**
+     * Only used for small battery use-case.
+     */
     @GuardedBy("mLock")
-    boolean mForceAllAppsStandby;   // True if device is in extreme battery saver mode
+    boolean mIsPluggedIn;
 
     @GuardedBy("mLock")
-    boolean mForcedAppStandbyEnabled;   // True if the forced app standby feature is enabled
+    boolean mBatterySaverEnabled;
 
-    private class FeatureFlagObserver extends ContentObserver {
-        FeatureFlagObserver() {
+    /**
+     * True if the forced app standby is currently enabled
+     */
+    @GuardedBy("mLock")
+    boolean mForceAllAppsStandby;
+
+    /**
+     * True if the forced app standby for small battery devices feature is enabled in settings
+     */
+    @GuardedBy("mLock")
+    boolean mForceAllAppStandbyForSmallBattery;
+
+    /**
+     * True if the forced app standby feature is enabled in settings
+     */
+    @GuardedBy("mLock")
+    boolean mForcedAppStandbyEnabled;
+
+    @VisibleForTesting
+    class FeatureFlagsObserver extends ContentObserver {
+        FeatureFlagsObserver() {
             super(null);
         }
 
@@ -128,6 +155,9 @@
             mContext.getContentResolver().registerContentObserver(
                     Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
                     false, this);
+
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
         }
 
         boolean isForcedAppStandbyEnabled() {
@@ -135,20 +165,43 @@
                     Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
         }
 
+        boolean isForcedAppStandbyForSmallBatteryEnabled() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+        }
+
         @Override
-        public void onChange(boolean selfChange) {
-            final boolean enabled = isForcedAppStandbyEnabled();
-            synchronized (mLock) {
-                if (mForcedAppStandbyEnabled == enabled) {
-                    return;
+        public void onChange(boolean selfChange, Uri uri) {
+            if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+                final boolean enabled = isForcedAppStandbyEnabled();
+                synchronized (mLock) {
+                    if (mForcedAppStandbyEnabled == enabled) {
+                        return;
+                    }
+                    mForcedAppStandbyEnabled = enabled;
+                    if (DEBUG) {
+                        Slog.d(TAG,"Forced app standby feature flag changed: "
+                                + mForcedAppStandbyEnabled);
+                    }
                 }
-                mForcedAppStandbyEnabled = enabled;
-                if (DEBUG) {
-                    Slog.d(TAG,
-                            "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+                mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+            } else if (Settings.Global.getUriFor(
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+                final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+                synchronized (mLock) {
+                    if (mForceAllAppStandbyForSmallBattery == enabled) {
+                        return;
+                    }
+                    mForceAllAppStandbyForSmallBattery = enabled;
+                    if (DEBUG) {
+                        Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+                                + mForceAllAppStandbyForSmallBattery);
+                    }
+                    updateForceAllAppStandbyState();
                 }
+            } else {
+                Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
             }
-            mHandler.notifyFeatureFlagChanged();
         }
     }
 
@@ -289,9 +342,11 @@
             mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
             mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
             mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
-            final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
-            flagObserver.register();
-            mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
+            mFlagsObserver = new FeatureFlagsObserver();
+            mFlagsObserver.register();
+            mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+            mForceAllAppStandbyForSmallBattery =
+                    mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
 
             try {
                 mIActivityManager.registerUidObserver(new UidObserver(),
@@ -306,16 +361,24 @@
 
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_USER_REMOVED);
+            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
             mContext.registerReceiver(new MyReceiver(), filter);
 
             refreshForcedAppStandbyUidPackagesLocked();
 
             mPowerManagerInternal.registerLowPowerModeObserver(
                     ServiceType.FORCE_ALL_APPS_STANDBY,
-                    (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+                    (state) -> {
+                        synchronized (mLock) {
+                            mBatterySaverEnabled = state.batterySaverEnabled;
+                            updateForceAllAppStandbyState();
+                        }
+                    });
 
-            updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
-                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+            mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
+
+            updateForceAllAppStandbyState();
         }
     }
 
@@ -340,6 +403,11 @@
         return LocalServices.getService(PowerManagerInternal.class);
     }
 
+    @VisibleForTesting
+    boolean isSmallBatteryDevice() {
+        return ActivityManager.isSmallBatteryDevice();
+    }
+
     /**
      * Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
      */
@@ -369,18 +437,26 @@
         }
     }
 
+    private void updateForceAllAppStandbyState() {
+        synchronized (mLock) {
+            if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) {
+                toggleForceAllAppsStandbyLocked(!mIsPluggedIn);
+            } else {
+                toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
+            }
+        }
+    }
+
     /**
      * Update {@link #mForceAllAppsStandby} and notifies the listeners.
      */
-    void updateForceAllAppsStandby(boolean enable) {
-        synchronized (mLock) {
-            if (enable == mForceAllAppsStandby) {
-                return;
-            }
-            mForceAllAppsStandby = enable;
-
-            mHandler.notifyForceAllAppsStandbyChanged();
+    private void toggleForceAllAppsStandbyLocked(boolean enable) {
+        if (enable == mForceAllAppsStandby) {
+            return;
         }
+        mForceAllAppsStandby = enable;
+
+        mHandler.notifyForceAllAppsStandbyChanged();
     }
 
     private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -515,6 +591,11 @@
                 if (userId > 0) {
                     mHandler.doUserRemoved(userId);
                 }
+            } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+                synchronized (mLock) {
+                    mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+                }
+                updateForceAllAppStandbyState();
             }
         }
     }
@@ -533,7 +614,7 @@
         private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
         private static final int MSG_FORCE_ALL_CHANGED = 6;
         private static final int MSG_USER_REMOVED = 7;
-        private static final int MSG_FEATURE_FLAG_CHANGED = 8;
+        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
 
         public MyHandler(Looper looper) {
             super(looper);
@@ -563,8 +644,8 @@
             obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
         }
 
-        public void notifyFeatureFlagChanged() {
-            obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+        public void notifyForcedAppStandbyFeatureFlagChanged() {
+            obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
         }
 
         public void doUserRemoved(int userId) {
@@ -618,7 +699,7 @@
                         l.onForceAllAppsStandbyChanged(sender);
                     }
                     return;
-                case MSG_FEATURE_FLAG_CHANGED:
+                case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
                     // Feature flag for forced app standby changed.
                     final boolean unblockAlarms;
                     synchronized (mLock) {
@@ -845,6 +926,18 @@
             pw.println(isForceAllAppsStandbyEnabled());
 
             pw.print(indent);
+            pw.print("Small Battery Device: ");
+            pw.println(isSmallBatteryDevice());
+
+            pw.print(indent);
+            pw.print("Force all apps standby for small battery device: ");
+            pw.println(mForceAllAppStandbyForSmallBattery);
+
+            pw.print(indent);
+            pw.print("Plugged In: ");
+            pw.println(mIsPluggedIn);
+
+            pw.print(indent);
             pw.print("Foreground uids: [");
 
             String sep = "";
@@ -883,6 +976,11 @@
             final long token = proto.start(fieldId);
 
             proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+            proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+                    isSmallBatteryDevice());
+            proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+                    mForceAllAppStandbyForSmallBattery);
+            proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn);
 
             for (int i = 0; i < mForegroundUids.size(); i++) {
                 if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 46a35ec..ef6bc43 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -148,7 +148,7 @@
          * resources.
          *
          * <p>References to the IResource object may be held by other RefcountedResource objects,
-         * and as such, the kernel resources and quota may not be cleaned up.
+         * and as such, the underlying resources and quota may not be cleaned up.
          */
         void invalidate() throws RemoteException;
 
@@ -298,7 +298,12 @@
         }
     }
 
-    /* Very simple counting class that looks much like a counting semaphore */
+    /**
+     * Very simple counting class that looks much like a counting semaphore
+     *
+     * <p>This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static class ResourceTracker {
         private final int mMax;
@@ -341,26 +346,38 @@
 
     @VisibleForTesting
     static final class UserRecord {
-        /* Type names */
-        public static final String TYPENAME_SPI = "SecurityParameterIndex";
-        public static final String TYPENAME_TRANSFORM = "IpSecTransform";
-        public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
-
         /* Maximum number of each type of resource that a single UID may possess */
         public static final int MAX_NUM_ENCAP_SOCKETS = 2;
         public static final int MAX_NUM_TRANSFORMS = 4;
         public static final int MAX_NUM_SPIS = 8;
 
+        /**
+         * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
+         * and explicit (user) reference management.
+         *
+         * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
+         *
+         * <p>Resources are removed from this array when the user releases their explicit reference
+         * by calling one of the releaseResource() methods.
+         */
         final RefcountedResourceArray<SpiRecord> mSpiRecords =
-                new RefcountedResourceArray<>(TYPENAME_SPI);
-        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
-
+                new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
         final RefcountedResourceArray<TransformRecord> mTransformRecords =
-                new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
-        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
-
+                new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
         final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
-                new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+                new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
+
+        /**
+         * Trackers for quotas for each of the OwnedResource types.
+         *
+         * <p>These trackers are separate from the resource arrays, since they are incremented and
+         * decremented at different points in time. Specifically, quota is only returned upon final
+         * resource deallocation (after all explicit and implicit references are released). Note
+         * that it is possible that calls to releaseResource() will not return the used quota if
+         * there are other resources that depend on (are parents of) the resource being released.
+         */
+        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
+        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
         final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
 
         void removeSpiRecord(int resourceId) {
@@ -395,11 +412,15 @@
         }
     }
 
+    /**
+     * This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static final class UserResourceTracker {
         private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
 
-        /** Never-fail getter that populates the list of UIDs as-needed */
+        /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
         public UserRecord getUserRecord(int uid) {
             checkCallerUid(uid);
 
@@ -428,18 +449,20 @@
     @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
 
     /**
-     * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+     * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
      * resources. It relies on a provided resourceId that should uniquely identify the kernel
      * resource. To use this class, the user should implement the invalidate() and
      * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
-     * tracking arrays and kernel resources, respectively
+     * tracking arrays and kernel resources, respectively.
+     *
+     * <p>This class associates kernel resources with the UID that owns and controls them.
      */
-    private abstract class KernelResourceRecord implements IResource {
+    private abstract class OwnedResourceRecord implements IResource {
         final int pid;
         final int uid;
         protected final int mResourceId;
 
-        KernelResourceRecord(int resourceId) {
+        OwnedResourceRecord(int resourceId) {
             super();
             if (resourceId == INVALID_RESOURCE_ID) {
                 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
@@ -479,8 +502,6 @@
         }
     };
 
-    // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
-    // more things as changed.
     /**
      * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
      *
@@ -534,7 +555,12 @@
         }
     }
 
-    private final class TransformRecord extends KernelResourceRecord {
+    /**
+     * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
+     * created, the SpiRecord that originally tracked the SAs will reliquish the
+     * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+     */
+    private final class TransformRecord extends OwnedResourceRecord {
         private final IpSecConfig mConfig;
         private final SpiRecord mSpi;
         private final EncapSocketRecord mSocket;
@@ -603,7 +629,12 @@
         }
     }
 
-    private final class SpiRecord extends KernelResourceRecord {
+    /**
+     * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
+     * responsibility for cleaning up underlying resources will be passed to the TransformRecord
+     * object
+     */
+    private final class SpiRecord extends OwnedResourceRecord {
         private final String mSourceAddress;
         private final String mDestinationAddress;
         private int mSpi;
@@ -692,7 +723,14 @@
         }
     }
 
-    private final class EncapSocketRecord extends KernelResourceRecord {
+    /**
+     * Tracks a UDP encap socket, and manages cleanup paths
+     *
+     * <p>While this class does not manage non-kernel resources, race conditions around socket
+     * binding require that the service creates the encap socket, binds it and applies the socket
+     * policy before handing it to a user.
+     */
+    private final class EncapSocketRecord extends OwnedResourceRecord {
         private FileDescriptor mSocket;
         private final int mPort;
 
@@ -1105,16 +1143,14 @@
      * receive data.
      */
     @Override
-    public synchronized IpSecTransformResponse createTransportModeTransform(
-            IpSecConfig c, IBinder binder) throws RemoteException {
+    public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
+            throws RemoteException {
         checkIpSecConfig(c);
-        checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
+        checkNotNull(binder, "Null Binder passed to createTransform");
         final int resourceId = mNextResourceId++;
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
-        // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
-        List<RefcountedResource> dependencies = new ArrayList<>(2);
+        List<RefcountedResource> dependencies = new ArrayList<>();
 
         if (!userRecord.mTransformQuotaTracker.isAvailable()) {
             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
@@ -1186,7 +1222,7 @@
      * other reasons.
      */
     @Override
-    public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+    public synchronized void deleteTransform(int resourceId) throws RemoteException {
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         releaseResource(userRecord.mTransformRecords, resourceId);
     }
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 80f8e51..833def3 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -26,4 +26,7 @@
 
     /** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */
     byte[] getFrpCredentialHandle();
+
+    /** Update the OEM unlock enabled bit, bypassing user restriction checks. */
+    void forceOemUnlockEnabled(boolean enabled);
 }
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index c32a2d1..4298140 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -668,5 +668,13 @@
                 IoUtils.closeQuietly(inputStream);
             }
         }
+
+        @Override
+        public void forceOemUnlockEnabled(boolean enabled) {
+            synchronized (mLock) {
+                doSetOemUnlockEnabledLocked(enabled);
+                computeAndWriteDigestLocked();
+            }
+        }
     };
 }
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
new file mode 100644
index 0000000..6c1ffdd
--- /dev/null
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.SystemUpdateManager.KEY_STATUS;
+import static android.os.SystemUpdateManager.STATUS_IDLE;
+import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ISystemUpdateManager;
+import android.os.PersistableBundle;
+import android.os.SystemUpdateManager;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class SystemUpdateManagerService extends ISystemUpdateManager.Stub {
+
+    private static final String TAG = "SystemUpdateManagerService";
+
+    private static final int UID_UNKNOWN = -1;
+
+    private static final String INFO_FILE = "system-update-info.xml";
+    private static final int INFO_FILE_VERSION = 0;
+    private static final String TAG_INFO = "info";
+    private static final String KEY_VERSION = "version";
+    private static final String KEY_UID = "uid";
+    private static final String KEY_BOOT_COUNT = "boot-count";
+    private static final String KEY_INFO_BUNDLE = "info-bundle";
+
+    private final Context mContext;
+    private final AtomicFile mFile;
+    private final Object mLock = new Object();
+    private int mLastUid = UID_UNKNOWN;
+    private int mLastStatus = STATUS_UNKNOWN;
+
+    public SystemUpdateManagerService(Context context) {
+        mContext = context;
+        mFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), INFO_FILE));
+
+        // Populate mLastUid and mLastStatus.
+        synchronized (mLock) {
+            loadSystemUpdateInfoLocked();
+        }
+    }
+
+    @Override
+    public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.RECOVERY, TAG);
+
+        int status = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+        if (status == STATUS_UNKNOWN) {
+            Slog.w(TAG, "Invalid status info. Ignored");
+            return;
+        }
+
+        // There could be multiple updater apps running on a device. But only one at most should
+        // be active (i.e. with a pending update), with the rest reporting idle status. We will
+        // only accept the reported status if any of the following conditions holds:
+        //   a) none has been reported before;
+        //   b) the current on-file status was last reported by the same caller;
+        //   c) an active update is being reported.
+        int uid = Binder.getCallingUid();
+        if (mLastUid == UID_UNKNOWN || mLastUid == uid || status != STATUS_IDLE) {
+            synchronized (mLock) {
+                saveSystemUpdateInfoLocked(infoBundle, uid);
+            }
+        } else {
+            Slog.i(TAG, "Inactive updater reporting IDLE status. Ignored");
+        }
+    }
+
+    @Override
+    public Bundle retrieveSystemUpdateInfo() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_SYSTEM_UPDATE_INFO)
+                == PackageManager.PERMISSION_DENIED
+                && mContext.checkCallingOrSelfPermission(Manifest.permission.RECOVERY)
+                == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException("Can't read system update info. Requiring "
+                    + "READ_SYSTEM_UPDATE_INFO or RECOVERY permission.");
+        }
+
+        synchronized (mLock) {
+            return loadSystemUpdateInfoLocked();
+        }
+    }
+
+    // Reads and validates the info file. Returns the loaded info bundle on success; or a default
+    // info bundle with UNKNOWN status.
+    private Bundle loadSystemUpdateInfoLocked() {
+        PersistableBundle loadedBundle = null;
+        try (FileInputStream fis = mFile.openRead()) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+            loadedBundle = readInfoFileLocked(parser);
+        } catch (FileNotFoundException e) {
+            Slog.i(TAG, "No existing info file " + mFile.getBaseFile());
+        } catch (XmlPullParserException e) {
+            Slog.e(TAG, "Failed to parse the info file:", e);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read the info file:", e);
+        }
+
+        // Validate the loaded bundle.
+        if (loadedBundle == null) {
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int version = loadedBundle.getInt(KEY_VERSION, -1);
+        if (version == -1) {
+            Slog.w(TAG, "Invalid info file (invalid version). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastUid = loadedBundle.getInt(KEY_UID, -1);
+        if (lastUid == -1) {
+            Slog.w(TAG, "Invalid info file (invalid UID). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastBootCount = loadedBundle.getInt(KEY_BOOT_COUNT, -1);
+        if (lastBootCount == -1 || lastBootCount != getBootCount()) {
+            Slog.w(TAG, "Outdated info file. Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        PersistableBundle infoBundle = loadedBundle.getPersistableBundle(KEY_INFO_BUNDLE);
+        if (infoBundle == null) {
+            Slog.w(TAG, "Invalid info file (missing info). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastStatus = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+        if (lastStatus == STATUS_UNKNOWN) {
+            Slog.w(TAG, "Invalid info file (invalid status). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        // Everything looks good upon reaching this point.
+        mLastStatus = lastStatus;
+        mLastUid = lastUid;
+        return new Bundle(infoBundle);
+    }
+
+    private void saveSystemUpdateInfoLocked(PersistableBundle infoBundle, int uid) {
+        // Wrap the incoming bundle with extra info (e.g. version, uid, boot count). We use nested
+        // PersistableBundle to avoid manually parsing XML attributes when loading the info back.
+        PersistableBundle outBundle = new PersistableBundle();
+        outBundle.putPersistableBundle(KEY_INFO_BUNDLE, infoBundle);
+        outBundle.putInt(KEY_VERSION, INFO_FILE_VERSION);
+        outBundle.putInt(KEY_UID, uid);
+        outBundle.putInt(KEY_BOOT_COUNT, getBootCount());
+
+        // Only update the info on success.
+        if (writeInfoFileLocked(outBundle)) {
+            mLastUid = uid;
+            mLastStatus = infoBundle.getInt(KEY_STATUS);
+        }
+    }
+
+    // Performs I/O work only, without validating the loaded info.
+    @Nullable
+    private PersistableBundle readInfoFileLocked(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != END_DOCUMENT) {
+            if (type == START_TAG && TAG_INFO.equals(parser.getName())) {
+                return PersistableBundle.restoreFromXml(parser);
+            }
+        }
+        return null;
+    }
+
+    private boolean writeInfoFileLocked(PersistableBundle outBundle) {
+        FileOutputStream fos = null;
+        try {
+            fos = mFile.startWrite();
+
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+
+            out.startTag(null, TAG_INFO);
+            outBundle.saveToXml(out);
+            out.endTag(null, TAG_INFO);
+
+            out.endDocument();
+            mFile.finishWrite(fos);
+            return true;
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Failed to save the info file:", e);
+            if (fos != null) {
+                mFile.failWrite(fos);
+            }
+        }
+        return false;
+    }
+
+    private Bundle removeInfoFileAndGetDefaultInfoBundleLocked() {
+        if (mFile.exists()) {
+            Slog.i(TAG, "Removing info file");
+            mFile.delete();
+        }
+
+        mLastStatus = STATUS_UNKNOWN;
+        mLastUid = UID_UNKNOWN;
+        Bundle infoBundle = new Bundle();
+        infoBundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
+        return infoBundle;
+    }
+
+    private int getBootCount() {
+        return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index e38148c..db21ef1 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -676,6 +676,15 @@
         }
     }
 
+    public void dumpStacks(PrintWriter pw) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            pw.print(mStacks.get(i).mStackId);
+            if (i > 0) {
+                pw.print(",");
+            }
+        }
+    }
+
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c0c684c..dc34567 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,9 +27,9 @@
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REMOVE_TASKS;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
@@ -120,6 +120,7 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -198,6 +199,7 @@
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -277,8 +279,8 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PathPermission;
 import android.content.pm.PermissionInfo;
@@ -356,18 +358,18 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.LongSparseArray;
-import android.util.StatsLog;
-import android.util.TimingsTraceLog;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
+import android.util.TimingsTraceLog;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
@@ -394,6 +396,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.BinderInternal;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.ByteTransferPipe;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
@@ -440,6 +443,12 @@
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -479,11 +488,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
 
@@ -8388,8 +8392,7 @@
                     stack.setPictureInPictureAspectRatio(aspectRatio);
                     stack.setPictureInPictureActions(actions);
 
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                            r.supportsEnterPipOnTaskSwitch);
+                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.supportsEnterPipOnTaskSwitch);
                     logPictureInPictureArgs(params);
                 };
 
@@ -10414,10 +10417,9 @@
 
     @Override
     public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
-        if (userId != UserHandle.getCallingUserId()) {
-            enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "getTaskDescriptionIcon");
-        }
+        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+
         final File passedIconFile = new File(filePath);
         final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
                 passedIconFile.getName());
@@ -21480,6 +21482,17 @@
     private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
         final Rect newStackBounds = new Rect();
         final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+        // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+        if (stack == null) {
+            final StringWriter writer = new StringWriter();
+            final PrintWriter printWriter = new PrintWriter(writer);
+            mStackSupervisor.dumpDisplays(printWriter);
+            printWriter.flush();
+
+            Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+        }
+
         stack.getBoundsForNewConfiguration(newStackBounds);
         mStackSupervisor.resizeStackLocked(
                 stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
@@ -25159,6 +25172,10 @@
         public int getMaxRunningUsers() {
             return mUserController.mMaxRunningUsers;
         }
+
+        public boolean isCallerRecents(int callingUid) {
+            return getRecentTasks().isCallerRecents(callingUid);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8168cba..eb14bbd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -167,7 +167,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
@@ -2620,8 +2620,7 @@
                 mAllowDockedStackResize = false;
             } else if (inPinnedWindowingMode && onTop) {
                 // Log if we are expanding the PiP to fullscreen
-                MetricsLogger.action(mService.mContext,
-                        ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+                MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext);
             }
 
             // If we are moving from the pinned stack, then the animation takes care of updating
@@ -3752,6 +3751,15 @@
         }
     }
 
+    public void dumpDisplays(PrintWriter pw) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.valueAt(i);
+            pw.print("[id:" + display.mDisplayId + " stacks:");
+            display.dumpStacks(pw);
+            pw.print("]");
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 8910274..26d65bc 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -745,7 +745,7 @@
                     mContext.getContentResolver(),
                     Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                     0,
-                    UserHandle.USER_CURRENT) != 0;
+                    mService.mUserController.getCurrentUserId()) != 0;
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
             if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1fcaeef..927b72c 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -99,7 +99,7 @@
     // Keep the last WiFi stats so we can compute a delta.
     @GuardedBy("mWorkerLock")
     private WifiActivityEnergyInfo mLastInfo =
-            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
+            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
 
     BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
         mContext = context;
@@ -374,6 +374,7 @@
 
     private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
         final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
+        final long lastScanMs = mLastInfo.mControllerScanTimeMs;
         final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
         final long lastTxMs = mLastInfo.mControllerTxTimeMs;
         final long lastRxMs = mLastInfo.mControllerRxTimeMs;
@@ -388,14 +389,16 @@
         final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
         final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
         final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
+        final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
 
-        if (txTimeMs < 0 || rxTimeMs < 0) {
+        if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) {
             // The stats were reset by the WiFi system (which is why our delta is negative).
             // Returns the unaltered stats.
             delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
             delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
             delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
             delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+            delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
             Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
         } else {
             final long totalActiveTimeMs = txTimeMs + rxTimeMs;
@@ -433,6 +436,7 @@
             // These times seem to be the most reliable.
             delta.mControllerTxTimeMs = txTimeMs;
             delta.mControllerRxTimeMs = rxTimeMs;
+            delta.mControllerScanTimeMs = scanTimeMs;
             // WiFi calculates the idle time as a difference from the on time and the various
             // Rx + Tx times. There seems to be some missing time there because this sometimes
             // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 207aaa7..04b49ba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.os.health.HealthStatsWriter;
@@ -1455,6 +1456,16 @@
     }
 
     /**
+     * Gets a snapshot of Wifi stats
+     * @hide
+     */
+    public WifiBatteryStats getWifiBatteryStats() {
+        synchronized (mStats) {
+            return mStats.getWifiBatteryStats();
+        }
+    }
+
+    /**
      * Gets a snapshot of Gps stats
      * @hide
      */
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1a47aa5..5ada484 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1431,7 +1431,13 @@
 
         if (callingUid != 0 && callingUid != SYSTEM_UID) {
             final boolean allow;
-            if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+            if (mInjector.isCallerRecents(callingUid)
+                    && callingUserId == getCurrentUserId()
+                    && isSameProfileGroup(callingUserId, targetUserId)) {
+                // If the caller is Recents and it is running in the current user, we then allow it
+                // to access its profiles.
+                allow = true;
+            } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
                     callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
                 // If the caller has this permission, they always pass go.  And collect $200.
                 allow = true;
@@ -2149,5 +2155,9 @@
                 mService.mLockTaskController.clearLockedTasks(reason);
             }
         }
+
+        protected boolean isCallerRecents(int callingUid) {
+            return mService.getRecentTasks().isCallerRecents(callingUid);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bd1dbf9..fa5fdf5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -63,6 +63,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -823,6 +824,8 @@
                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
             }
             startTrackingJobLocked(jobStatus, toCancel);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
+                    uId, null, jobStatus.getBatteryName(), 2);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 724073a..452c9ee 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -36,6 +36,7 @@
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
@@ -301,7 +302,8 @@
      */
     private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
-            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
         PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index c33c9de..d85e89e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -184,7 +184,8 @@
     public static Map<String, SecretKey> unwrapKeys(
             PlatformDecryptionKey platformKey,
             Map<String, WrappedKey> wrappedKeys)
-            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
         Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
         int platformKeyGenerationId = platformKey.getGenerationId();
@@ -201,20 +202,10 @@
                         platformKey.getGenerationId()));
             }
 
-            try {
-                cipher.init(
-                        Cipher.UNWRAP_MODE,
-                        platformKey.getKey(),
-                        new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
-            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
-                Log.e(TAG,
-                        String.format(
-                                Locale.US,
-                                "Could not init Cipher to unwrap recoverable key with alias '%s'",
-                                alias),
-                        e);
-                continue;
-            }
+            cipher.init(
+                    Cipher.UNWRAP_MODE,
+                    platformKey.getKey(),
+                    new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
             SecretKey key;
             try {
                 key = (SecretKey) cipher.unwrap(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
new file mode 100644
index 0000000..b25eaa7
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -0,0 +1,181 @@
+/*
+ * 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 com.android.server.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.MediaSession2;
+import android.media.SessionToken;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Records a {@link MediaSession2} and holds {@link MediaController2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSession2Record {
+    interface SessionDestroyedListener {
+        void onSessionDestroyed(MediaSession2Record record);
+    }
+
+    private static final String TAG = "Session2Record";
+    private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+    private final Context mContext;
+    private final SessionDestroyedListener mSessionDestroyedListener;
+
+    // TODO(jaewan): Replace these with the mContext.getMainExecutor()
+    private final Handler mMainHandler;
+    private final Executor mMainExecutor;
+
+    private MediaController2 mController;
+    private ControllerCallback mControllerCallback;
+
+    private int mSessionPid;
+
+    /**
+     * Constructor
+     */
+    public MediaSession2Record(@NonNull Context context,
+            @NonNull SessionDestroyedListener listener) {
+        mContext = context;
+        mSessionDestroyedListener = listener;
+
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mMainExecutor = (runnable) -> {
+            mMainHandler.post(runnable);
+        };
+    }
+
+    public int getSessionPid() {
+        return mSessionPid;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    @CallSuper
+    public void onSessionDestroyed() {
+        if (mController != null) {
+            mControllerCallback.destroy();
+            mController.release();
+            mController = null;
+        }
+        mSessionPid = 0;
+    }
+
+    /**
+     * Create session token and tell server that session is now active.
+     *
+     * @param sessionPid session's pid
+     * @return a token if successfully set, {@code null} if sanity check fails.
+     */
+    // TODO(jaewan): also add uid for multiuser support
+    @CallSuper
+    public @Nullable
+    SessionToken createSessionToken(int sessionPid, String packageName, String id,
+            IMediaSession2 sessionBinder) {
+        if (mController != null) {
+            if (mSessionPid != sessionPid) {
+                // A package uses the same id for session across the different process.
+                return null;
+            }
+            // If a session becomes inactive and then active again very quickly, previous 'inactive'
+            // may not have delivered yet. Check if it's the case and destroy controller before
+            // creating its session record to prevents getXXTokens() API from returning duplicated
+            // tokens.
+            // TODO(jaewan): Change this. If developer is really creating two sessions with the same
+            //               id, this will silently invalidate previous session and no way for
+            //               developers to know that.
+            //               Instead, keep the list of static session ids from our APIs.
+            //               Also change Controller2Impl.onConnectionChanged / getController.
+            //               Also clean up ControllerCallback#destroy().
+            if (DEBUG) {
+                Log.d(TAG, "Session is recreated almost immediately. " + this);
+            }
+            onSessionDestroyed();
+        }
+        mController = onCreateMediaController(packageName, id, sessionBinder);
+        mSessionPid = sessionPid;
+        return mController.getSessionToken();
+    }
+
+    /**
+     * Called when session becomes active and needs controller to listen session's activeness.
+     * <p>
+     * Should be overridden by subclasses to create token with its own extra information.
+     */
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(
+                SessionToken.TYPE_SESSION, packageName, id, null, sessionBinder);
+        return createMediaController(token);
+    }
+
+    final MediaController2 createMediaController(SessionToken token) {
+        mControllerCallback = new ControllerCallback();
+        return new MediaController2(mContext, token, mControllerCallback, mMainExecutor);
+    }
+
+    /**
+     * @return controller. Note that framework can only call oneway calls.
+     */
+    public SessionToken getToken() {
+        return mController == null ? null : mController.getSessionToken();
+    }
+
+    @Override
+    public String toString() {
+        return getToken() == null
+                ? "Token {null}"
+                : "SessionRecord {pid=" + mSessionPid + ", " + getToken().toString() + "}";
+    }
+
+    private class ControllerCallback extends MediaController2.ControllerCallback {
+        private final AtomicBoolean mIsActive = new AtomicBoolean(true);
+
+        // This is called on the main thread with no lock. So place ensure followings.
+        //   1. Don't touch anything in the parent class that needs synchronization.
+        //      All other APIs in the MediaSession2Record assumes that server would use them with
+        //      the lock hold.
+        //   2. This can be called after the controller registered is released.
+        @Override
+        public void onDisconnected() {
+            if (!mIsActive.get()) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "onDisconnected, token=" + getToken());
+            }
+            mSessionDestroyedListener.onSessionDestroyed(MediaSession2Record.this);
+        }
+
+        // TODO(jaewan): Remove this API when we revisit createSessionToken()
+        public void destroy() {
+            mIsActive.set(false);
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 06f4f5e..6812778 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -28,13 +28,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IAudioService;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -118,6 +123,24 @@
     // better way to handle this.
     private IRemoteVolumeController mRvc;
 
+    // MediaSession2 support
+    // TODO(jaewan): Support multi-user and managed profile.
+    // TODO(jaewan): Make it priority list for handling volume/media key.
+    private final List<MediaSession2Record> mSessions = new ArrayList<>();
+
+    private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener =
+            (MediaSession2Record record) -> {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Log.d(TAG, record.toString() + " becomes inactive");
+                    }
+                    record.onSessionDestroyed();
+                    if (!(record instanceof MediaSessionService2Record)) {
+                        mSessions.remove(record);
+                    }
+                }
+            };
+
     public MediaSessionService(Context context) {
         super(context);
         mSessionManagerImpl = new SessionManagerImpl();
@@ -158,6 +181,11 @@
                 PackageManager.FEATURE_LEANBACK);
 
         updateUser();
+
+        // TODO(jaewan): Query per users
+        // TODO(jaewan): Add listener to know changes in list of services.
+        //               Refer TvInputManagerService.registerBroadcastReceivers()
+        buildMediaSessionService2List();
     }
 
     private IAudioService getAudioService() {
@@ -411,6 +439,64 @@
         mHandler.postSessionsChanged(session.getUserId());
     }
 
+    private void buildMediaSessionService2List() {
+        if (DEBUG) {
+            Log.d(TAG, "buildMediaSessionService2List");
+        }
+
+        // TODO(jaewan): Query per users.
+        List<ResolveInfo> services = getContext().getPackageManager().queryIntentServices(
+                new Intent(MediaSessionService2.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA);
+        synchronized (mLock) {
+            mSessions.clear();
+            if (services == null) {
+                return;
+            }
+            for (int i = 0; i < services.size(); i++) {
+                if (services.get(i) == null || services.get(i).serviceInfo == null) {
+                    continue;
+                }
+                ServiceInfo serviceInfo = services.get(i).serviceInfo;
+                String id = (serviceInfo.metaData != null) ? serviceInfo.metaData.getString(
+                        MediaSessionService2.SERVICE_META_DATA) : null;
+                // Do basic sanity check
+                // TODO(jaewan): also santity check if it's protected with the system|privileged
+                //               permission
+                boolean conflict = (getSessionRecordLocked(serviceInfo.name, id) != null);
+                if (conflict) {
+                    Log.w(TAG, serviceInfo.packageName + " contains multiple"
+                            + " MediaSessionService2s declared in the manifest with"
+                            + " the same ID=" + id + ". Ignoring "
+                            + serviceInfo.packageName + "/" + serviceInfo.name);
+                } else {
+                    MediaSessionService2Record record =
+                            new MediaSessionService2Record(getContext(), mSessionDestroyedListener,
+                                    SessionToken.TYPE_SESSION_SERVICE,
+                                    serviceInfo.packageName, serviceInfo.name, id);
+                    mSessions.add(record);
+                }
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Found " + mSessions.size() + " session services");
+            for (int i = 0; i < mSessions.size(); i++) {
+                Log.d(TAG, "   " + mSessions.get(i).getToken());
+            }
+        }
+    }
+
+    MediaSession2Record getSessionRecordLocked(String packageName, String id) {
+        for (int i = 0; i < mSessions.size(); i++) {
+            MediaSession2Record record = mSessions.get(i);
+            if (record.getToken().getPackageName().equals(packageName)
+                    && record.getToken().getId().equals(id)) {
+                return record;
+            }
+        }
+        return null;
+    }
+
     private void enforcePackageName(String packageName, int uid) {
         if (TextUtils.isEmpty(packageName)) {
             throw new IllegalArgumentException("packageName may not be empty");
@@ -1312,6 +1398,57 @@
             }
         }
 
+        @Override
+        public Bundle createSessionToken(String sessionPackage, String id,
+                IMediaSession2 sessionBinder) throws RemoteException {
+            int uid = Binder.getCallingUid();
+            int pid = Binder.getCallingPid();
+
+            MediaSession2Record record;
+            SessionToken token;
+            // TODO(jaewan): Add sanity check for the token if calling package is from uid.
+            synchronized (mLock) {
+                record = getSessionRecordLocked(sessionPackage, id);
+                if (record == null) {
+                    record = new MediaSession2Record(getContext(), mSessionDestroyedListener);
+                    mSessions.add(record);
+                }
+                token = record.createSessionToken(pid, sessionPackage, id, sessionBinder);
+                if (token == null) {
+                    Log.d(TAG, "failed to create session token for " + sessionPackage
+                            + " from pid=" + pid + ". Previously " + record);
+                } else {
+                    Log.d(TAG, "session " + token + " is created");
+                }
+            }
+            return token == null ? null : token.toBundle();
+        }
+
+        // TODO(jaewan): Protect this API with permission
+        // TODO(jaewan): Add listeners for change in operations..
+        @Override
+        public List<Bundle> getSessionTokens(boolean activeSessionOnly,
+                boolean sessionServiceOnly) throws RemoteException {
+            List<Bundle> tokens = new ArrayList<>();
+            synchronized (mLock) {
+                for (int i = 0; i < mSessions.size(); i++) {
+                    MediaSession2Record record = mSessions.get(i);
+                    boolean isSessionService = (record instanceof MediaSessionService2Record);
+                    boolean isActive = record.getSessionPid() != 0;
+                    if ((!activeSessionOnly && isSessionService)
+                            || (!sessionServiceOnly && isActive)) {
+                        SessionToken token = record.getToken();
+                        if (token != null) {
+                            tokens.add(token.toBundle());
+                        } else {
+                            Log.wtf(TAG, "Null token for record=" + record);
+                        }
+                    }
+                }
+            }
+            return tokens;
+        }
+
         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
                 final int uid) {
             String packageName = null;
diff --git a/services/core/java/com/android/server/media/MediaSessionService2Record.java b/services/core/java/com/android/server/media/MediaSessionService2Record.java
new file mode 100644
index 0000000..bd97dbc
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionService2Record.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.android.server.media;
+
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.SessionToken;
+import android.media.MediaSessionService2;
+
+/**
+ * Records a {@link MediaSessionService2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSessionService2Record extends MediaSession2Record {
+    private static final boolean DEBUG = true; // TODO(jaewan): Modify
+    private static final String TAG = "SessionService2Record";
+
+    private final int mType;
+    private final String mServiceName;
+    private final SessionToken mToken;
+
+    public MediaSessionService2Record(Context context,
+            SessionDestroyedListener sessionDestroyedListener, int type,
+            String packageName, String serviceName, String id) {
+        super(context, sessionDestroyedListener);
+        mType = type;
+        mServiceName = serviceName;
+        mToken = new SessionToken(mType, packageName, id, mServiceName, null);
+    }
+
+    /**
+     * Overriden to change behavior of
+     * {@link #createSessionToken(int, String, String, IMediaSession2)}}.
+     */
+    @Override
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(mType, packageName, id, mServiceName, sessionBinder);
+        return createMediaController(token);
+    }
+
+    /**
+     * @return token with no session binder information.
+     */
+    @Override
+    public SessionToken getToken() {
+        return mToken;
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 239ddbe..7165e60 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net.watchlist;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
@@ -210,6 +211,12 @@
         return stopWatchlistLoggingImpl();
     }
 
+    @Nullable
+    @Override
+    public byte[] getWatchlistConfigHash() {
+        return mConfig.getWatchlistConfigHash();
+    }
+
     private void enforceWatchlistLoggingPermission() {
         final int uid = Binder.getCallingUid();
         if (uid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index fbf11fc..7387ad4 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -161,7 +161,7 @@
     public boolean containsDomain(String domain) {
         final CrcShaDigests domainDigests = mDomainDigests;
         if (domainDigests == null) {
-            Slog.wtf(TAG, "domainDigests should not be null");
+            // mDomainDigests is not initialized
             return false;
         }
         // First it does a quick CRC32 check.
@@ -177,7 +177,7 @@
     public boolean containsIp(String ip) {
         final CrcShaDigests ipDigests = mIpDigests;
         if (ipDigests == null) {
-            Slog.wtf(TAG, "ipDigests should not be null");
+            // mIpDigests is not initialized
             return false;
         }
         // First it does a quick CRC32 check.
@@ -234,12 +234,20 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Domain CRC32 digest list:");
-        mDomainDigests.crc32Digests.dump(fd, pw, args);
+        if (mDomainDigests != null) {
+            mDomainDigests.crc32Digests.dump(fd, pw, args);
+        }
         pw.println("Domain SHA256 digest list:");
-        mDomainDigests.sha256Digests.dump(fd, pw, args);
+        if (mDomainDigests != null) {
+            mDomainDigests.sha256Digests.dump(fd, pw, args);
+        }
         pw.println("Ip CRC32 digest list:");
-        mIpDigests.crc32Digests.dump(fd, pw, args);
+        if (mIpDigests != null) {
+            mIpDigests.crc32Digests.dump(fd, pw, args);
+        }
         pw.println("Ip SHA256 digest list:");
-        mIpDigests.sha256Digests.dump(fd, pw, args);
+        if (mIpDigests != null) {
+            mIpDigests.sha256Digests.dump(fd, pw, args);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2f6618f..39b7c7c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -137,6 +137,7 @@
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.NotificationServiceDumpProto;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -329,6 +330,7 @@
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+    final ArrayMap<Integer, ArrayList<NotifyingApp>> mRecentApps = new ArrayMap<>();
 
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
@@ -2110,6 +2112,16 @@
         }
 
         @Override
+        public ParceledListSlice<NotifyingApp> getRecentNotifyingAppsForUser(int userId) {
+            checkCallerIsSystem();
+            synchronized (mNotificationLock) {
+                List<NotifyingApp> apps = new ArrayList<>(
+                        mRecentApps.getOrDefault(userId, new ArrayList<>()));
+                return new ParceledListSlice<>(apps);
+            }
+        }
+
+        @Override
         public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
             checkCallerIsSystem();
 
@@ -4096,6 +4108,10 @@
 
                     mNotificationsByKey.put(n.getKey(), r);
 
+                    if (!r.isUpdate) {
+                        logRecentLocked(r);
+                    }
+
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -4153,6 +4169,38 @@
     }
 
     /**
+     * Keeps the last 5 packages that have notified, by user.
+     */
+    @GuardedBy("mNotificationLock")
+    @VisibleForTesting
+    protected void logRecentLocked(NotificationRecord r) {
+        if (r.isUpdate) {
+            return;
+        }
+        ArrayList<NotifyingApp> recentAppsForUser =
+                mRecentApps.getOrDefault(r.getUser().getIdentifier(), new ArrayList<>(6));
+        NotifyingApp na = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        // A new notification gets an app moved to the front of the list
+        for (int i = recentAppsForUser.size() - 1; i >= 0; i--) {
+            NotifyingApp naExisting = recentAppsForUser.get(i);
+            if (na.getPackage().equals(naExisting.getPackage())
+                    && na.getUid() == naExisting.getUid()) {
+                recentAppsForUser.remove(i);
+                break;
+            }
+        }
+        // time is always increasing, so always add to the front of the list
+        recentAppsForUser.add(0, na);
+        if (recentAppsForUser.size() > 5) {
+            recentAppsForUser.remove(recentAppsForUser.size() -1);
+        }
+        mRecentApps.put(r.getUser().getIdentifier(), recentAppsForUser);
+    }
+
+    /**
      * Ensures that grouped notification receive their special treatment.
      *
      * <p>Cancels group children if the new notification causes a group to lose
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 2a2ff06..a6200bf 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -31,10 +31,10 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.service.oemlock.IOemLockService;
-import android.service.persistentdata.PersistentDataBlockManager;
 import android.util.Slog;
 
 import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.SystemService;
 import com.android.server.pm.UserRestrictionsUtils;
 
@@ -217,13 +217,12 @@
      * is used to erase FRP information on a unlockable device.
      */
     private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
-        final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
-                mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+        final PersistentDataBlockManagerInternal pdbmi
+                = LocalServices.getService(PersistentDataBlockManagerInternal.class);
         // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
-        if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)
-                && pdbm.getOemUnlockEnabled() != allowed) {
+        if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) {
             Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
-            pdbm.setOemUnlockEnabled(allowed);
+            pdbmi.forceOemUnlockEnabled(allowed);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index cc448da..a6ff4f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -344,9 +344,13 @@
         final boolean isSelfUpdatePermissionGranted =
                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+        final boolean isUpdatePermissionGranted =
+                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
+                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+        final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
         final boolean isPermissionGranted = isInstallPermissionGranted
-                || (isSelfUpdatePermissionGranted
-                    && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
+                || (isUpdatePermissionGranted && targetPackageUid != -1)
+                || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
         final boolean forcePermissionPrompt =
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 10b377d..faf6114 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -118,6 +118,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.ResourcesManager;
@@ -992,6 +993,7 @@
     private List<String> mKeepUninstalledPackages;
 
     private UserManagerInternal mUserManagerInternal;
+    private ActivityManagerInternal mActivityManagerInternal;
 
     private DeviceIdleController.LocalService mDeviceIdleController;
 
@@ -4575,6 +4577,14 @@
         return mUserManagerInternal;
     }
 
+    private ActivityManagerInternal getActivityManagerInternal() {
+        if (mActivityManagerInternal == null) {
+            mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        }
+        return mActivityManagerInternal;
+    }
+
+
     private DeviceIdleController.LocalService getDeviceIdleController() {
         if (mDeviceIdleController == null) {
             mDeviceIdleController =
@@ -4735,8 +4745,12 @@
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, component);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                false /* requireFullPermission */, false /* checkShell */, "get activity info");
+
+        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                    false /* requireFullPermission */, false /* checkShell */, "get activity info");
+        }
+
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
 
@@ -4758,6 +4772,22 @@
         return null;
     }
 
+    private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
+        if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            if (ActivityManager.getCurrentUser() != callingUserId) {
+                return false;
+            }
+            return sUserManager.isSameProfileGroup(callingUserId, targetUserId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public boolean activitySupportsIntent(ComponentName component, Intent intent,
             String resolvedType) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a33f071..47cd813 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -236,6 +236,8 @@
                     return runHasFeature();
                 case "set-harmful-app-warning":
                     return runSetHarmfulAppWarning();
+                case "get-harmful-app-warning":
+                    return runGetHarmfulAppWarning();
                 default: {
                     String nextArg = getNextArg();
                     if (nextArg == null) {
@@ -2125,6 +2127,31 @@
         return 0;
     }
 
+    private int runGetHarmfulAppWarning() throws RemoteException {
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+
+        userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning");
+
+        final String packageName = getNextArgRequired();
+        final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId);
+        if (!TextUtils.isEmpty(warning)) {
+            getOutPrintWriter().println(warning);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
     private static String checkAbiArgument(String abi) {
         if (TextUtils.isEmpty(abi)) {
             throw new IllegalArgumentException("Missing ABI argument");
@@ -2684,6 +2711,9 @@
         pw.println("");
         pw.println("  set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
         pw.println("    Mark the app as harmful with the given warning message.");
+        pw.println("");
+        pw.println("  get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
+        pw.println("    Return the harmful app warning message for the given app, if present");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6599fd9..0f394a4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4352,6 +4352,7 @@
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
             Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
+        final int pfl = attrs.privateFlags;
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
         final int displayRotation = displayFrames.mRotation;
@@ -4374,8 +4375,12 @@
             }
         }
 
-        if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
-                == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+        final boolean layoutInScreenAndInsetDecor =
+                (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
+                        == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR);
+        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+
+        if (layoutInScreenAndInsetDecor && !screenDecor) {
             Rect frame;
             int availRight, availBottom;
             if (canHideNavigationBar() &&
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 09f6da9..5811714 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -21,6 +21,8 @@
 import android.content.ContentProviderClient;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -49,11 +51,13 @@
     @GuardedBy("mLock")
     private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
     @GuardedBy("mLock")
-    private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
+    private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>();
     @GuardedBy("mLock")
     private SliceSpec[] mSupportedSpecs = null;
     @GuardedBy("mLock")
-    private final ArrayMap<ISliceListener, String> mPkgMap = new ArrayMap<>();
+    private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();
+
+    private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;
 
     public PinnedSliceState(SliceManagerService service, Uri uri) {
         mService = service;
@@ -107,20 +111,27 @@
 
     public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
         synchronized (mLock) {
-            if (mListeners.add(listener) && mListeners.size() == 1) {
+            if (mListeners.size() == 0) {
                 mService.listen(mUri);
             }
-            mPkgMap.put(listener, pkg);
+            try {
+                listener.asBinder().linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+            }
+            mListeners.put(listener.asBinder(), listener);
+            mPkgMap.put(listener.asBinder(), pkg);
             mergeSpecs(specs);
         }
     }
 
     public boolean removeSliceListener(ISliceListener listener) {
         synchronized (mLock) {
-            mPkgMap.remove(listener);
-            if (mListeners.remove(listener) && mListeners.size() == 0) {
+            listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+            mPkgMap.remove(listener.asBinder());
+            if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
                 mService.unlisten(mUri);
             }
+            mListeners.remove(listener.asBinder());
         }
         return !isPinned();
     }
@@ -159,25 +170,44 @@
         return client;
     }
 
+    private void handleRecheckListeners() {
+        if (!isPinned()) return;
+        synchronized (mLock) {
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                ISliceListener l = mListeners.valueAt(i);
+                if (!l.asBinder().isBinderAlive()) {
+                    mListeners.removeAt(i);
+                }
+            }
+            if (!isPinned()) {
+                // All the listeners died, remove from pinned state.
+                mService.removePinnedSlice(mUri);
+            }
+        }
+    }
+
     private void handleBind() {
         Slice cachedSlice = doBind(null);
         synchronized (mLock) {
-            mListeners.removeIf(l -> {
+            if (!isPinned()) return;
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                ISliceListener l = mListeners.valueAt(i);
                 Slice s = cachedSlice;
                 if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
                     s = doBind(mPkgMap.get(l));
                 }
                 if (s == null) {
-                    return true;
+                    mListeners.removeAt(i);
+                    continue;
                 }
                 try {
                     l.onSliceUpdated(s);
-                    return false;
                 } catch (RemoteException e) {
                     Log.e(TAG, "Unable to notify slice " + mUri, e);
-                    return true;
+                    mListeners.removeAt(i);
+                    continue;
                 }
-            });
+            }
             if (!isPinned()) {
                 // All the listeners died, remove from pinned state.
                 mService.removePinnedSlice(mUri);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index ca7632c..c191580 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -91,9 +91,9 @@
 
         mObserver = new ContentObserver(mHandler) {
             @Override
-            public void onChange(boolean selfChange, Uri uri) {
+            public void onChange(boolean selfChange, Uri uri, int userId) {
                 try {
-                    getPinnedSlice(uri).onChange();
+                    getPinnedSlice(maybeAddUserId(uri, userId)).onChange();
                 } catch (IllegalStateException e) {
                     Log.e(TAG, "Received change for unpinned slice " + uri, e);
                 }
@@ -204,7 +204,7 @@
     }
 
     ///  ----- internal code -----
-    void removePinnedSlice(Uri uri) {
+    protected void removePinnedSlice(Uri uri) {
         synchronized (mLock) {
             mPinnedSlicesByUri.remove(uri).destroy();
         }
@@ -233,7 +233,7 @@
     }
 
     @VisibleForTesting
-    PinnedSliceState createPinnedSlice(Uri uri) {
+    protected PinnedSliceState createPinnedSlice(Uri uri) {
         return new PinnedSliceState(this, uri);
     }
 
@@ -352,7 +352,7 @@
     // Based on getDefaultHome in ShortcutService.
     // TODO: Unify if possible
     @VisibleForTesting
-    String getDefaultHome(int userId) {
+    protected String getDefaultHome(int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
             final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4cb5e08..f82dc24 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -28,12 +28,10 @@
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
-import android.os.SystemClock;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
@@ -41,18 +39,22 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatFs;
 import android.os.StatsLogEventWrapper;
 import android.os.SynchronousResultReceiver;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
-import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.PowerProfile;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -97,6 +99,11 @@
     private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
     private IWifiManager mWifiManager = null;
     private TelephonyManager mTelephony = null;
+    private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+    private final StatFs mStatFsSystem =
+        new StatFs(Environment.getRootDirectory().getAbsolutePath());
+    private final StatFs mStatFsTemp =
+        new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
 
     public StatsCompanionService(Context context) {
         super();
@@ -560,7 +567,7 @@
                     if (clusterTimeMs != null) {
                         for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
                             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
-                            e.writeInt(tagId);
+                            e.writeInt(cluster);
                             e.writeInt(speed);
                             e.writeLong(clusterTimeMs[speed]);
                             ret.add(e);
@@ -589,6 +596,7 @@
                         e.writeLong(wifiInfo.getControllerIdleTimeMillis());
                         e.writeLong(wifiInfo.getControllerEnergyUsed());
                         ret.add(e);
+                        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
                     } finally {
@@ -619,6 +627,7 @@
                     e.writeLong(modemInfo.getRxTimeMillis());
                     e.writeLong(modemInfo.getEnergyUsed());
                     ret.add(e);
+                    return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                 }
                 break;
             }
@@ -627,14 +636,30 @@
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
                 e.writeLong(SystemClock.elapsedRealtime());
                 ret.add(e);
-                break;
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
             case StatsLog.CPU_IDLE_TIME: {
                 List<StatsLogEventWrapper> ret = new ArrayList();
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
                 e.writeLong(SystemClock.uptimeMillis());
                 ret.add(e);
-                break;
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.DISK_SPACE: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+              e.writeLong(mStatFsData.getAvailableBytes());
+              e.writeLong(mStatFsSystem.getAvailableBytes());
+              e.writeLong(mStatFsTemp.getAvailableBytes());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.SYSTEM_UPTIME: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+              e.writeLong(SystemClock.uptimeMillis());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b474c62..ce3f512 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -463,10 +463,14 @@
                 mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
             }
 
-            // Update the client visibility if we are not running an animation. Otherwise, we'll
-            // update client visibility state in onAnimationFinished.
-            if (!visible && !delayed) {
-                setClientHidden(true);
+            // If we're becoming visible, immediately change client visibility as well although it
+            // usually gets changed in AppWindowContainerController.setVisibility already. However,
+            // there seem to be some edge cases where we change our visibility but client visibility
+            // never gets updated.
+            // If we're becoming invisible, update the client visibility if we are not running an
+            // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+            if (visible || !delayed) {
+                setClientHidden(!visible);
             }
 
             // If we are hidden but there is no delay needed we immediately
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 688b4ff..8515dcb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -99,11 +99,15 @@
     }
 
     private RemoteAnimationTarget[] createAnimations() {
-        final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+        final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+            final RemoteAnimationTarget target =
+                    mPendingAnimations.get(i).createRemoteAppAnimation();
+            if (target != null) {
+                targets.add(target);
+            }
         }
-        return result;
+        return targets.toArray(new RemoteAnimationTarget[targets.size()]);
     }
 
     private void onAnimationFinished() {
@@ -145,9 +149,17 @@
         }
 
         RemoteAnimationTarget createRemoteAppAnimation() {
-            return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+            final Task task = mAppWindowToken.getTask();
+            final WindowState mainWindow = mAppWindowToken.findMainWindow();
+            if (task == null) {
+                return null;
+            }
+            if (mainWindow == null) {
+                return null;
+            }
+            return new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
-                    mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+                    mainWindow.mWinAnimator.mLastClipRect,
                     mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
         }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e660c50..94a356e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1172,6 +1172,15 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("StartSystemUpdateManagerService");
+            try {
+                ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
+                        new SystemUpdateManagerService(context));
+            } catch (Throwable e) {
+                reportWtf("starting SystemUpdateManagerService", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("StartUpdateLockService");
             try {
                 ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 429dd8f..de54e52 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager.ServiceType;
@@ -51,6 +52,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
@@ -105,6 +107,9 @@
         PowerManagerInternal injectPowerManagerInternal() {
             return mMockPowerManagerInternal;
         }
+
+        @Override
+        boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
     }
 
     private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -144,6 +149,7 @@
     private FakeSettingsProvider mFakeSettingsProvider;
 
     private boolean mPowerSaveMode;
+    private boolean mIsSmallBatteryDevice;
 
     private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
 
@@ -232,6 +238,7 @@
         assertNotNull(mAppOpsCallback);
         assertNotNull(mPowerSaveObserver);
         assertNotNull(mReceiver);
+        assertNotNull(instance.mFlagsObserver);
     }
 
     private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -852,6 +859,56 @@
         assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
     }
 
+    @Test
+    public void testSmallBatteryAndPluggedIn() throws Exception {
+        // This is a small battery device
+        mIsSmallBatteryDevice = true;
+
+        final ForceAppStandbyTrackerTestable instance = newInstance();
+        callStart(instance);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        // Setting/experiment for all app standby for small battery is enabled
+        Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+        instance.mFlagsObserver.onChange(true,
+                Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery is plugged in, force app standby is disabled
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        mReceiver.onReceive(mMockContext, intent);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery stops plugged in, force app standby is enabled
+        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+    }
+
+    @Test
+    public void testNotSmallBatteryAndPluggedIn() throws Exception {
+        // Not a small battery device, so plugged in status should not affect forced app standby
+        mIsSmallBatteryDevice = false;
+
+        final ForceAppStandbyTrackerTestable instance = newInstance();
+        callStart(instance);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        mPowerSaveMode = true;
+        mPowerSaveObserver.accept(getPowerSaveState());
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery is plugged in, force app standby is unaffected
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        mReceiver.onReceive(mMockContext, intent);
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery stops plugged in, force app standby is unaffected
+        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+    }
+
     static int[] array(int... appIds) {
         Arrays.sort(appIds);
         return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 7c3082f..045b73c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -134,7 +134,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -185,7 +184,6 @@
     "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
  * </code></pre>
  */
-@Ignore
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class NetworkPolicyManagerServiceTest {
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index a31b46c..999dce5 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -77,9 +77,9 @@
         assertEquals(6, result.size());
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
-        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
-        assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
-        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
+        assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
         assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
     }
 
@@ -87,7 +87,7 @@
     public void testPrivacyUtils_createInsecureDPEncoderForTest() throws Exception {
         DifferentialPrivacyEncoder encoder = PrivacyUtils.createInsecureDPEncoderForTest("foo");
         assertEquals(
-                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
                         + "ProbabilityQ: 1.000",
                 encoder.getConfig().toString());
         assertTrue(encoder.isInsecureEncoderForTest());
@@ -97,7 +97,7 @@
     public void testPrivacyUtils_createSecureDPEncoderTest() throws Exception {
         DifferentialPrivacyEncoder encoder = PrivacyUtils.createSecureDPEncoder(TEST_SECRET, "foo");
         assertEquals(
-                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
                         + "ProbabilityQ: 1.000",
                 encoder.getConfig().toString());
         assertFalse(encoder.isInsecureEncoderForTest());
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 851d2c6..654acc2 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -33,12 +33,15 @@
 import org.junit.runner.RunWith;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintWriter;
 import java.util.Arrays;
 
+
 /**
  * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests
  */
@@ -117,6 +120,15 @@
         assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
     }
 
+    @Test
+    public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
+        WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
+        ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
+        PrintWriter pw = new PrintWriter(bs);
+        // Make sure dump still works even watchlist does not exist
+        config.dump(null, pw, null);
+    }
+
     private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
             throws IOException {
         writeToFile(outFile, readAsset(context, xmlAsset));
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index b6c370e..293f9af 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -27,14 +27,19 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
 import android.view.WindowManager;
 
 import org.junit.Before;
@@ -262,4 +267,23 @@
         assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
     }
 
+    @Test
+    public void insetHint_screenDecorWindow() {
+        addDisplayCutout();
+        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect content = new Rect();
+        final Rect stable = new Rect();
+        final Rect outsets = new Rect();
+        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
+        mPolicy.getInsetHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, content,
+                stable, outsets, cutout);
+
+        assertThat(content, equalTo(new Rect()));
+        assertThat(stable, equalTo(new Rect()));
+        assertThat(outsets, equalTo(new Rect()));
+        assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
+    }
 }
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ad3fecf..e0bebee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -73,6 +73,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -105,8 +106,10 @@
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -253,10 +256,19 @@
         mFile.delete();
     }
 
-    public void waitForIdle() throws Exception {
+    public void waitForIdle() {
         mTestableLooper.processAllMessages();
     }
 
+    private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
+        Notification.Builder nb = new Notification.Builder(mContext, "a")
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0,
+                nb.build(), new UserHandle(userId), null, postTime);
+        return sbn;
+    }
+
     private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
             String groupKey, boolean isSummary) {
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
@@ -2291,4 +2303,102 @@
 
         verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
     }
+
+    @Test
+    public void testRecents() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+    }
+
+    @Test
+    public void testRecentsNoDuplicatePackages() throws Exception {
+        final NotificationRecord p1 = new NotificationRecord(mContext, generateSbn("p", 1, 1000, 0),
+                mTestNotificationChannel);
+        final NotificationRecord p2 = new NotificationRecord(mContext, generateSbn("p", 1, 2000, 0),
+                mTestNotificationChannel);
+
+        mService.logRecentLocked(p1);
+        mService.logRecentLocked(p2);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 1);
+        NotifyingApp expected = new NotifyingApp().setPackage("p").setUid(1).setLastNotified(2000);
+        assertEquals(expected, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsWithDuplicatePackage() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+        NotificationRecord r = new NotificationRecord(mContext,
+                generateSbn("p" + 3, 3, 300000, 0), mTestNotificationChannel);
+        expected.remove(new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(3)
+                .setLastNotified(300));
+        NotifyingApp newest = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        expected.add(newest);
+        mService.logRecentLocked(r);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+        assertEquals(newest, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsMultiuser() throws Exception {
+        final NotificationRecord user1 = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 1), mTestNotificationChannel);
+        mService.logRecentLocked(user1);
+
+        final NotificationRecord user2 = new NotificationRecord(mContext,
+                generateSbn("p2", 100000, 9999, 2), mTestNotificationChannel);
+        mService.logRecentLocked(user2);
+
+        assertEquals(0, mBinderService.getRecentNotifyingAppsForUser(0).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(1).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(2).getList().size());
+
+        assertTrue(mBinderService.getRecentNotifyingAppsForUser(2).getList().contains(
+                new NotifyingApp()
+                        .setPackage(user2.sbn.getPackageName())
+                        .setUid(user2.sbn.getUid())
+                        .setLastNotified(user2.sbn.getPostTime())));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
index 4f153ee..0a630f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package com.android.server.notification;
 
 import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
new file mode 100644
index 0000000..fbb8c33
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.notification.NotifyingApp;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotifyingAppTest extends UiServiceTestCase {
+
+    @Test
+    public void testConstructor() {
+        NotifyingApp na = new NotifyingApp();
+        assertEquals(0, na.getUid());
+        assertEquals(0, na.getLastNotified());
+        assertEquals(null, na.getPackage());
+    }
+
+    @Test
+    public void testPackage() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("test");
+        assertEquals("test", na.getPackage());
+    }
+
+    @Test
+    public void testUid() {
+        NotifyingApp na = new NotifyingApp();
+        na.setUid(90);
+        assertEquals(90, na.getUid());
+    }
+
+    @Test
+    public void testLastNotified() {
+        NotifyingApp na = new NotifyingApp();
+        na.setLastNotified((long) 8000);
+        assertEquals((long) 8000, na.getLastNotified());
+    }
+
+    @Test
+    public void testWriteToParcel() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("package");
+        na.setUid(200);
+        na.setLastNotified(4000);
+
+        Parcel parcel = Parcel.obtain();
+        na.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotifyingApp na1 = NotifyingApp.CREATOR.createFromParcel(parcel);
+        assertEquals(na.getLastNotified(), na1.getLastNotified());
+        assertEquals(na.getPackage(), na1.getPackage());
+        assertEquals(na.getUid(), na1.getUid());
+    }
+
+    @Test
+    public void testCompareTo() {
+        NotifyingApp na1 = new NotifyingApp();
+        na1.setPackage("pkg1");
+        na1.setUid(1000);
+        na1.setLastNotified(6);
+
+        NotifyingApp na2 = new NotifyingApp();
+        na2.setPackage("a");
+        na2.setUid(999);
+        na2.setLastNotified(1);
+
+        assertTrue(na1.compareTo(na2) < 0);
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index aada682..d3bb804 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -21,8 +22,11 @@
 import android.content.ContentProvider;
 import android.content.IContentProvider;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -34,6 +38,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -147,6 +152,7 @@
     @Test
     public void testListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
         assertFalse(mPinnedSliceManager.isPinned());
 
         mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -159,7 +165,11 @@
     @Test
     public void testMultiListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
+        Binder value = new Binder();
+        when(listener.asBinder()).thenReturn(value);
         ISliceListener listener2 = mock(ISliceListener.class);
+        Binder value2 = new Binder();
+        when(listener2.asBinder()).thenReturn(value2);
         assertFalse(mPinnedSliceManager.isPinned());
 
         mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -172,8 +182,30 @@
     }
 
     @Test
+    public void testListenerDeath() throws RemoteException {
+        ISliceListener listener = mock(ISliceListener.class);
+        IBinder binder = mock(IBinder.class);
+        when(binder.isBinderAlive()).thenReturn(true);
+        when(listener.asBinder()).thenReturn(binder);
+        assertFalse(mPinnedSliceManager.isPinned());
+
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
+        assertTrue(mPinnedSliceManager.isPinned());
+
+        ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
+        verify(binder).linkToDeath(arg.capture(), anyInt());
+
+        when(binder.isBinderAlive()).thenReturn(false);
+        arg.getValue().binderDied();
+
+        verify(mSliceService).removePinnedSlice(eq(TEST_URI));
+        assertFalse(mPinnedSliceManager.isPinned());
+    }
+
+    @Test
     public void testPkgListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
         assertFalse(mPinnedSliceManager.isPinned());
 
         mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
@@ -191,6 +223,7 @@
         clearInvocations(mIContentProvider);
 
         ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
         Slice s = new Slice.Builder(TEST_URI).build();
         Bundle b = new Bundle();
         b.putParcelable(SliceProvider.EXTRA_SLICE, s);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c7d6b3..d17bdc8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -419,7 +419,6 @@
         /**
          * Indicates the call used Assisted Dialing.
          * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
-         * @hide
          */
         public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index aaef8d3..7522443 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -35,6 +35,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -401,7 +402,6 @@
 
     /**
      * Set by the framework to indicate that a connection is using assisted dialing.
-     * @hide
      */
     public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
 
@@ -2538,6 +2538,19 @@
     }
 
     /**
+     * Adds a parcelable extra to this {@code Connection}.
+     *
+     * @param key The extra key.
+     * @param value The value.
+     * @hide
+     */
+    public final void putExtra(@NonNull String key, @NonNull Parcelable value) {
+        Bundle newExtras = new Bundle();
+        newExtras.putParcelable(key, value);
+        putExtras(newExtras);
+    }
+
+    /**
      * Removes extras from this {@code Connection}.
      *
      * @param keys The keys of the extras to remove.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d292db3..91d5da3 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -602,12 +602,17 @@
     /**
      * The boolean indicated by this extra controls whether or not a call is eligible to undergo
      * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
-     * @hide
      */
     public static final String EXTRA_USE_ASSISTED_DIALING =
             "android.telecom.extra.USE_ASSISTED_DIALING";
 
     /**
+     * The bundle indicated by this extra store information related to the assisted dialing action.
+     */
+    public static final String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO =
+            "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telecomm/java/android/telecom/TransformationInfo.java b/telecomm/java/android/telecom/TransformationInfo.java
new file mode 100755
index 0000000..3e848c6
--- /dev/null
+++ b/telecomm/java/android/telecom/TransformationInfo.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A container class to hold information related to the Assisted Dialing operation. All member
+ * variables must be set when constructing a new instance of this class.
+ */
+public final class TransformationInfo implements Parcelable {
+    private String mOriginalNumber;
+    private String mTransformedNumber;
+    private String mUserHomeCountryCode;
+    private String mUserRoamingCountryCode;
+    private int mTransformedNumberCountryCallingCode;
+
+    public TransformationInfo(String originalNumber,
+                              String transformedNumber,
+                              String userHomeCountryCode,
+                              String userRoamingCountryCode,
+                              int transformedNumberCountryCallingCode) {
+        String missing = "";
+        if (originalNumber == null) {
+            missing += " mOriginalNumber";
+        }
+        if (transformedNumber == null) {
+            missing += " mTransformedNumber";
+        }
+        if (userHomeCountryCode == null) {
+            missing += " mUserHomeCountryCode";
+        }
+        if (userRoamingCountryCode == null) {
+            missing += " mUserRoamingCountryCode";
+        }
+
+        if (!missing.isEmpty()) {
+            throw new IllegalStateException("Missing required properties:" + missing);
+        }
+        this.mOriginalNumber = originalNumber;
+        this.mTransformedNumber = transformedNumber;
+        this.mUserHomeCountryCode = userHomeCountryCode;
+        this.mUserRoamingCountryCode = userRoamingCountryCode;
+        this.mTransformedNumberCountryCallingCode = transformedNumberCountryCallingCode;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mOriginalNumber);
+        out.writeString(mTransformedNumber);
+        out.writeString(mUserHomeCountryCode);
+        out.writeString(mUserRoamingCountryCode);
+        out.writeInt(mTransformedNumberCountryCallingCode);
+    }
+
+    public static final Parcelable.Creator<TransformationInfo> CREATOR
+            = new Parcelable.Creator<TransformationInfo>() {
+        public TransformationInfo createFromParcel(Parcel in) {
+            return new TransformationInfo(in);
+        }
+
+        public TransformationInfo[] newArray(int size) {
+            return new TransformationInfo[size];
+        }
+    };
+
+    private TransformationInfo(Parcel in) {
+        mOriginalNumber = in.readString();
+        mTransformedNumber = in.readString();
+        mUserHomeCountryCode = in.readString();
+        mUserRoamingCountryCode = in.readString();
+        mTransformedNumberCountryCallingCode = in.readInt();
+    }
+
+    /**
+     * The original number that underwent Assisted Dialing.
+     */
+    public String getOriginalNumber() {
+        return mOriginalNumber;
+    }
+
+    /**
+     * The number after it underwent Assisted Dialing.
+     */
+    public String getTransformedNumber() {
+        return mTransformedNumber;
+    }
+
+    /**
+     * The user's home country code that was used when attempting to transform the number.
+     */
+    public String getUserHomeCountryCode() {
+        return mUserHomeCountryCode;
+    }
+
+    /**
+     * The users's roaming country code that was used when attempting to transform the number.
+     */
+    public String getUserRoamingCountryCode() {
+        return mUserRoamingCountryCode;
+    }
+
+    /**
+     * The country calling code that was used in the transformation.
+     */
+    public int getTransformedNumberCountryCallingCode() {
+        return mTransformedNumberCountryCallingCode;
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ce0b551..649d478 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1644,6 +1644,13 @@
             "roaming_operator_string_array";
 
     /**
+     * Controls whether Assisted Dialing is enabled and the preference is shown. This feature
+     * transforms numbers when the user is roaming.
+     */
+    public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL =
+            "assisted_dialing_enabled_bool";
+
+    /**
      * URL from which the proto containing the public key of the Carrier used for
      * IMSI encryption will be downloaded.
      * @hide
@@ -2040,6 +2047,7 @@
                 false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index de9e691..e33b25e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1024,8 +1024,8 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
-     * the updated carrier id {@link TelephonyManager#getSubscriptionCarrierId()} of the current
-     * subscription.
+     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the current subscription.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
      */
@@ -2136,8 +2136,8 @@
     public static final int SIM_STATE_PRESENT = 11;
 
     /**
-     * Extra included in {@link Intent.ACTION_SIM_CARD_STATE_CHANGED} and
-     * {@link Intent.ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
+     * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
      *
      * @hide
      */
@@ -2145,6 +2145,89 @@
     public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
 
     /**
+     * Broadcast Action: The sim card state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim card state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_ABSENT}</dt>
+     *       <dd>SIM card not found</dd>
+     *       <dt>{@link #SIM_STATE_CARD_IO_ERROR}</dt>
+     *       <dd>SIM card IO error</dd>
+     *       <dt>{@link #SIM_STATE_CARD_RESTRICTED}</dt>
+     *       <dd>SIM card is restricted</dd>
+     *       <dt>{@link #SIM_STATE_PRESENT}</dt>
+     *       <dd>SIM card is present</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using {@link #getSimCardState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_CARD_STATE_CHANGED =
+            "android.telephony.action.SIM_CARD_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The sim application state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim application state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_NOT_READY}</dt>
+     *       <dd>SIM card applications not ready</dd>
+     *       <dt>{@link #SIM_STATE_PIN_REQUIRED}</dt>
+     *       <dd>SIM card PIN locked</dd>
+     *       <dt>{@link #SIM_STATE_PUK_REQUIRED}</dt>
+     *       <dd>SIM card PUK locked</dd>
+     *       <dt>{@link #SIM_STATE_NETWORK_LOCKED}</dt>
+     *       <dd>SIM card network locked</dd>
+     *       <dt>{@link #SIM_STATE_PERM_DISABLED}</dt>
+     *       <dd>SIM card permanently disabled due to PUK failures</dd>
+     *       <dt>{@link #SIM_STATE_LOADED}</dt>
+     *       <dd>SIM card data loaded</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using
+     * {@link #getSimApplicationState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
+            "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: Status of the SIM slots on the device has changed.
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The status can be queried using
+     * {@link #getUiccSlotsInfo()}
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_SLOT_STATUS_CHANGED =
+            "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+
+    /**
      * @return true if a ICC card is present
      */
     public boolean hasIccCard() {
@@ -6900,14 +6983,19 @@
 
     /**
      * Returns carrier id of the current subscription.
-     * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier
-     * with a canonical integer a.k.a carrier id.
+     * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+     * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
+     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     *
+     * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+     * as an Android platform-wide identifier for carriers.
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
      * @throws IllegalStateException if telephony service is unavailable.
      */
-    public int getSubscriptionCarrierId() {
+    public int getAndroidCarrierIdForSubscription() {
         try {
             ITelephony service = getITelephony();
             return service.getSubscriptionCarrierId(getSubId());
@@ -6923,17 +7011,18 @@
 
     /**
      * Returns carrier name of the current subscription.
-     * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId()},
-     * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
-     * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
-     * Carrier name is not a canonical identity, use {@link #getSubscriptionCarrierId()} instead.
+     * <p>Carrier name is a user-facing name of carrier id
+     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+     * should have a single carrier name. Carrier name is not a canonical identity,
+     * use {@link #getAndroidCarrierIdForSubscription()} instead.
      * <p>The returned carrier name is unlocalized.
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
      * @throws IllegalStateException if telephony service is unavailable.
      */
-    public String getSubscriptionCarrierName() {
+    public CharSequence getAndroidCarrierNameForSubscription() {
         try {
             ITelephony service = getITelephony();
             return service.getSubscriptionCarrierName(getSubId());
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 2ab8d4f..73a05af 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -21,22 +21,23 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
-import android.net.NetworkUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
-import java.net.URL;
 import java.net.InetAddress;
-import java.util.Arrays;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -67,8 +68,8 @@
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
-    private final int mBearer;
-    private final int mBearerBitmask;
+
+    private final int mNetworkTypeBitmask;
 
     private final int mProfileId;
 
@@ -103,34 +104,6 @@
     }
 
     /**
-     * Radio Access Technology info.
-     * To check what values can hold, refer to ServiceState.java.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the bearer info of the APN
-     * @hide
-     */
-    public int getBearer() {
-        return mBearer;
-    }
-
-    /**
-     * Returns the radio access technology bitmask for this APN.
-     *
-     * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
-     * technologies in ServiceState.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the radio access technology bitmask
-     * @hide
-     */
-    public int getBearerBitmask() {
-        return mBearerBitmask;
-    }
-
-    /**
      * Returns the profile id to which the APN saved in modem.
      *
      * @return the profile id of the APN
@@ -411,6 +384,20 @@
         return mCarrierEnabled;
     }
 
+    /**
+     * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+     *
+     * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+     *
+     * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+     *
+     * @return a bitmask describing the Radio Technologies(Network Types)
+     */
+    public int getNetworkTypeBitmask() {
+        return mNetworkTypeBitmask;
+    }
+
     /** @hide */
     @StringDef({
             MVNO_TYPE_SPN,
@@ -452,8 +439,7 @@
         this.mRoamingProtocol = builder.mRoamingProtocol;
         this.mMtu = builder.mMtu;
         this.mCarrierEnabled = builder.mCarrierEnabled;
-        this.mBearer = builder.mBearer;
-        this.mBearerBitmask = builder.mBearerBitmask;
+        this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
         this.mProfileId = builder.mProfileId;
         this.mModemCognitive = builder.mModemCognitive;
         this.mMaxConns = builder.mMaxConns;
@@ -467,8 +453,8 @@
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
             String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
             int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-            int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
+            String protocol, String roamingProtocol, boolean carrierEnabled,
+            int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
             int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
@@ -487,8 +473,7 @@
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
-                .setBearer(bearer)
-                .setBearerBitmask(bearerBitmask)
+                .setNetworkTypeBitmask(networkTypeBitmask)
                 .setProfileId(profileId)
                 .setModemCognitive(modemCognitive)
                 .setMaxConns(maxConns)
@@ -504,6 +489,14 @@
     public static ApnSetting makeApnSetting(Cursor cursor) {
         String[] types = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+        int networkTypeBitmask = cursor.getInt(
+                cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+        if (networkTypeBitmask == 0) {
+            final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+                    Telephony.Carriers.BEARER_BITMASK));
+            networkTypeBitmask =
+                    ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+        }
 
         return makeApnSetting(
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
@@ -529,9 +522,7 @@
                         Telephony.Carriers.ROAMING_PROTOCOL)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.BEARER_BITMASK)),
+                networkTypeBitmask,
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -551,7 +542,7 @@
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
                 apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
                 apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
-                apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId,
+                apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
     }
@@ -559,7 +550,7 @@
     /** @hide */
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV3] ")
+        sb.append("[ApnSettingV4] ")
                 .append(mEntryName)
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
@@ -579,8 +570,6 @@
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
-        sb.append(", ").append(mBearer);
-        sb.append(", ").append(mBearerBitmask);
         sb.append(", ").append(mProfileId);
         sb.append(", ").append(mModemCognitive);
         sb.append(", ").append(mMaxConns);
@@ -590,6 +579,7 @@
         sb.append(", ").append(mMvnoType);
         sb.append(", ").append(mMvnoMatchData);
         sb.append(", ").append(mPermanentFailed);
+        sb.append(", ").append(mNetworkTypeBitmask);
         return sb.toString();
     }
 
@@ -678,8 +668,6 @@
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(mBearer, other.mBearer)
-                && Objects.equals(mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(mProfileId, other.mProfileId)
                 && Objects.equals(mModemCognitive, other.mModemCognitive)
                 && Objects.equals(mMaxConns, other.mMaxConns)
@@ -687,13 +675,14 @@
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
                 && Objects.equals(mMtu, other.mMtu)
                 && Objects.equals(mMvnoType, other.mMvnoType)
-                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     /**
      * Compare two APN settings
      *
-     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
      * determining if tearing a data call is needed when conditions change. See
      * cleanUpConnectionsOnUpdatedApns in DcTracker.
      *
@@ -752,13 +741,13 @@
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(this.mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
                 && xorEqualsURL(this.mMmsc, other.mMmsc)
                 && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort));
+                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     // Equal or one is not specified.
@@ -808,53 +797,33 @@
         return TextUtils.join(",", types);
     }
 
+    private String nullToEmpty(String stringValue) {
+        return stringValue == null ? "" : stringValue;
+    }
+
     /** @hide */
     // Called by DPM.
     public ContentValues toContentValues() {
         ContentValues apnValue = new ContentValues();
-        if (mOperatorNumeric != null) {
-            apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
-        }
-        if (mEntryName != null) {
-            apnValue.put(Telephony.Carriers.NAME, mEntryName);
-        }
-        if (mApnName != null) {
-            apnValue.put(Telephony.Carriers.APN, mApnName);
-        }
-        if (mProxy != null) {
-            apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
-        }
+        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
         apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        if (mMmsc != null) {
-            apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
-        }
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
         apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        if (mMmsProxy != null) {
-            apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
-        }
-        if (mUser != null) {
-            apnValue.put(Telephony.Carriers.USER, mUser);
-        }
-        if (mPassword != null) {
-            apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
-        }
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
+                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
         String apnType = deParseTypes(mTypes);
-        if (apnType != null) {
-            apnValue.put(Telephony.Carriers.TYPE, apnType);
-        }
-        if (mProtocol != null) {
-            apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
-        }
-        if (mRoamingProtocol != null) {
-            apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
-        }
+        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        // networkTypeBit.
-        apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
-        if (mMvnoType != null) {
-            apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
-        }
+        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
     }
@@ -905,8 +874,16 @@
         if (inetAddress == null) {
             return null;
         }
-        return TextUtils.isEmpty(inetAddress.getHostName())
-                ? inetAddress.getHostAddress() : inetAddress.getHostName();
+        final String inetAddressString = inetAddress.toString();
+        if (TextUtils.isEmpty(inetAddressString)) {
+            return null;
+        }
+        final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+        final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+        if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+            return null;
+        }
+        return TextUtils.isEmpty(hostName) ? address : hostName;
     }
 
     private static int portFromString(String strPort) {
@@ -952,16 +929,33 @@
         dest.writeString(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
         dest.writeString(mMvnoType);
+        dest.writeInt(mNetworkTypeBitmask);
     }
 
     private static ApnSetting readFromParcel(Parcel in) {
-        return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), in.readString(), in.readString(), in.readInt(),
-                Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
-                in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null);
+        final int id = in.readInt();
+        final String operatorNumeric = in.readString();
+        final String entryName = in.readString();
+        final String apnName = in.readString();
+        final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int port = in.readInt();
+        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int mmsPort = in.readInt();
+        final String user = in.readString();
+        final String password = in.readString();
+        final int authType = in.readInt();
+        final List<String> types = Arrays.asList(in.readStringArray());
+        final String protocol = in.readString();
+        final String roamingProtocol = in.readString();
+        final boolean carrierEnabled = in.readInt() > 0;
+        final String mvnoType = in.readString();
+        final int networkTypeBitmask = in.readInt();
+
+        return makeApnSetting(id, operatorNumeric, entryName, apnName,
+                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
+                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+                0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -1061,9 +1055,8 @@
         private String mProtocol;
         private String mRoamingProtocol;
         private int mMtu;
+        private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
-        private int mBearer;
-        private int mBearerBitmask;
         private int mProfileId;
         private boolean mModemCognitive;
         private int mMaxConns;
@@ -1078,6 +1071,16 @@
         public Builder() {}
 
         /**
+         * Sets the unique database id for this entry.
+         *
+         * @param id the unique database id to set for this entry
+         */
+        private Builder setId(int id) {
+            this.mId = id;
+            return this;
+        }
+
+        /**
          * Set the MTU size of the mobile interface to which the APN connected.
          *
          * @param mtu the MTU size to set for the APN
@@ -1089,28 +1092,6 @@
         }
 
         /**
-         * Sets bearer info.
-         *
-         * @param bearer the bearer info to set for the APN
-         * @hide
-         */
-        public Builder setBearer(int bearer) {
-            this.mBearer = bearer;
-            return this;
-        }
-
-        /**
-         * Sets the radio access technology bitmask for this APN.
-         *
-         * @param bearerBitmask the radio access technology bitmask to set for this APN
-         * @hide
-         */
-        public Builder setBearerBitmask(int bearerBitmask) {
-            this.mBearerBitmask = bearerBitmask;
-            return this;
-        }
-
-        /**
          * Sets the profile id to which the APN saved in modem.
          *
          * @param profileId the profile id to set for the APN
@@ -1298,16 +1279,6 @@
         }
 
         /**
-         * Sets the unique database id for this entry.
-         *
-         * @param id the unique database id to set for this entry
-         */
-        public Builder setId(int id) {
-            this.mId = id;
-            return this;
-        }
-
-        /**
          * Set the numeric operator ID for the APN.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
@@ -1341,7 +1312,7 @@
         }
 
         /**
-         * Sets the current status of APN.
+         * Sets the current status for this APN.
          *
          * @param carrierEnabled the current status to set for this APN
          */
@@ -1351,6 +1322,16 @@
         }
 
         /**
+         * Sets Radio Technology (Network Type) info for this APN.
+         *
+         * @param networkTypeBitmask the Radio Technology (Network Type) info
+         */
+        public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+            this.mNetworkTypeBitmask = networkTypeBitmask;
+            return this;
+        }
+
+        /**
          * Sets the MVNO match type for this APN.
          *
          * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2534327..d146707 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -71,8 +71,18 @@
      * TODO(b/35851809): Make this a SystemApi.
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_OTA_STATUS_CHANGED
-            = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+    public static final String ACTION_OTA_STATUS_CHANGED =
+            "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+    /**
+     * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+     * completed.
+     *
+     * TODO(b/35851809): Make this a public API.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFY_CARRIER_SETUP =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
 
     /**
      * Intent action to provision an embedded subscription.
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 1bd1af5..994f3cc 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -31,32 +31,34 @@
 LOCAL_CERTIFICATE := platform
 
 # These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
+LOCAL_JNI_SHARED_LIBRARIES := \
+    android.hidl.token@1.0 \
     libbacktrace \
     libbase \
     libbinder \
     libc++ \
+    libcrypto \
     libcutils \
+    libframeworksnettestsjni \
+    libhidl-gen-utils \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     liblog \
     liblzma \
     libnativehelper \
     libnetdaidl \
-    libui \
-    libunwind \
-    libutils \
-    libvndksupport \
-    libcrypto \
-    libhidl-gen-utils \
-    libhidlbase \
-    libhidltransport \
     libpackagelistparser \
     libpcre2 \
     libselinux \
-    libtinyxml2 \
+    libui \
+    libunwind \
+    libutils \
     libvintf \
-    libhwbinder \
+    libvndksupport \
+    libtinyxml2 \
     libunwindstack \
-    android.hidl.token@1.0
+    libutilscallstack
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4fbb228..d9d4eeb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -204,13 +204,13 @@
     }
 
     @Test
-    public void testCreateTransportModeTransform() throws Exception {
+    public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -236,14 +236,14 @@
     }
 
     @Test
-    public void testCreateTransportModeTransformAead() throws Exception {
+    public void testCreateTransformAead() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
 
         ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -269,14 +269,14 @@
     }
 
     @Test
-    public void testDeleteTransportModeTransform() throws Exception {
+    public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        mIpSecService.deleteTransform(createTransformResp.resourceId);
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
@@ -302,7 +302,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -334,7 +334,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
 
         int resourceId = createTransformResp.resourceId;
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 3eba881..a375b60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -166,6 +166,7 @@
         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
         udpEncapResp.fileDescriptor.close();
 
+        // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
@@ -179,10 +180,8 @@
 
     @Test
     public void testUdpEncapsulationSocketBinderDeath() throws Exception {
-        int localport = findUnusedPort();
-
         IpSecUdpEncapResponse udpEncapResp =
-                mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+                mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -192,6 +191,7 @@
 
         refcountedRecord.binderDied();
 
+        // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
         try {
             userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -412,9 +412,9 @@
     }
 
     @Test
-    public void testDeleteInvalidTransportModeTransform() throws Exception {
+    public void testDeleteInvalidTransform() throws Exception {
         try {
-            mIpSecService.deleteTransportModeTransform(1);
+            mIpSecService.deleteTransform(1);
             fail("IllegalArgumentException not thrown");
         } catch (IllegalArgumentException e) {
         }
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..9f2cb92 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,6 +66,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -174,6 +175,7 @@
     }
 
     @Test
+    @Ignore
     public void testDefaultNetworkEvents() throws Exception {
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -292,6 +294,7 @@
     }
 
     @Test
+    @Ignore
     public void testEndToEndLogging() throws Exception {
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 29bf02c..03c9fbe 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -56,6 +56,11 @@
     /**
      * @hide
      */
+    public long mControllerScanTimeMs;
+
+    /**
+     * @hide
+     */
     public long mControllerIdleTimeMs;
 
     /**
@@ -69,13 +74,14 @@
     public static final int STACK_STATE_STATE_IDLE = 3;
 
     public WifiActivityEnergyInfo(long timestamp, int stackState,
-                                  long txTime, long[] txTimePerLevel, long rxTime, long idleTime,
-                                  long energyUsed) {
+                                  long txTime, long[] txTimePerLevel, long rxTime, long scanTime,
+                                  long idleTime, long energyUsed) {
         mTimestamp = timestamp;
         mStackState = stackState;
         mControllerTxTimeMs = txTime;
         mControllerTxTimePerLevelMs = txTimePerLevel;
         mControllerRxTimeMs = rxTime;
+        mControllerScanTimeMs = scanTime;
         mControllerIdleTimeMs = idleTime;
         mControllerEnergyUsed = energyUsed;
     }
@@ -88,6 +94,7 @@
             + " mControllerTxTimeMs=" + mControllerTxTimeMs
             + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
             + " mControllerRxTimeMs=" + mControllerRxTimeMs
+            + " mControllerScanTimeMs=" + mControllerScanTimeMs
             + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
             + " mControllerEnergyUsed=" + mControllerEnergyUsed
             + " }";
@@ -101,10 +108,11 @@
             long txTime = in.readLong();
             long[] txTimePerLevel = in.createLongArray();
             long rxTime = in.readLong();
+            long scanTime = in.readLong();
             long idleTime = in.readLong();
             long energyUsed = in.readLong();
             return new WifiActivityEnergyInfo(timestamp, stackState,
-                    txTime, txTimePerLevel, rxTime, idleTime, energyUsed);
+                    txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed);
         }
         public WifiActivityEnergyInfo[] newArray(int size) {
             return new WifiActivityEnergyInfo[size];
@@ -117,6 +125,7 @@
         out.writeLong(mControllerTxTimeMs);
         out.writeLongArray(mControllerTxTimePerLevelMs);
         out.writeLong(mControllerRxTimeMs);
+        out.writeLong(mControllerScanTimeMs);
         out.writeLong(mControllerIdleTimeMs);
         out.writeLong(mControllerEnergyUsed);
     }
@@ -157,6 +166,13 @@
     }
 
     /**
+     * @return scan time in ms
+     */
+    public long getControllerScanTimeMillis() {
+        return mControllerScanTimeMs;
+    }
+
+    /**
      * @return idle time in ms
      */
     public long getControllerIdleTimeMillis() {
@@ -183,6 +199,7 @@
     public boolean isValid() {
         return ((mControllerTxTimeMs >=0) &&
                 (mControllerRxTimeMs >=0) &&
+                (mControllerScanTimeMs >=0) &&
                 (mControllerIdleTimeMs >=0));
     }
-}
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 99080d6..e4b510d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1625,6 +1625,7 @@
      *
      * @return hex-string encoded configuration token or null if there is no current network
      * @hide
+     * @deprecated This API is deprecated
      */
     public String getCurrentNetworkWpsNfcConfigurationToken() {
         try {
@@ -2210,20 +2211,34 @@
     /** @hide */
     public static final int SAVE_NETWORK_SUCCEEDED          = BASE + 9;
 
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int START_WPS                       = BASE + 10;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int START_WPS_SUCCEEDED             = BASE + 11;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int WPS_FAILED                      = BASE + 12;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int WPS_COMPLETED                   = BASE + 13;
 
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS                      = BASE + 14;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS_FAILED               = BASE + 15;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS_SUCCEDED             = BASE + 16;
 
     /** @hide */
@@ -2263,15 +2278,25 @@
     public static final int BUSY                        = 2;
 
     /* WPS specific errors */
-    /** WPS overlap detected */
+    /** WPS overlap detected
+     * @deprecated This is deprecated
+     */
     public static final int WPS_OVERLAP_ERROR           = 3;
-    /** WEP on WPS is prohibited */
+    /** WEP on WPS is prohibited
+     * @deprecated This is deprecated
+     */
     public static final int WPS_WEP_PROHIBITED          = 4;
-    /** TKIP only prohibited */
+    /** TKIP only prohibited
+     * @deprecated This is deprecated
+     */
     public static final int WPS_TKIP_ONLY_PROHIBITED    = 5;
-    /** Authentication failure on WPS */
+    /** Authentication failure on WPS
+     * @deprecated This is deprecated
+     */
     public static final int WPS_AUTH_FAILURE            = 6;
-    /** WPS timed out */
+    /** WPS timed out
+     * @deprecated This is deprecated
+     */
     public static final int WPS_TIMED_OUT               = 7;
 
     /**
@@ -2312,12 +2337,19 @@
         public void onFailure(int reason);
     }
 
-    /** Interface for callback invocation on a start WPS action */
+    /** Interface for callback invocation on a start WPS action
+     * @deprecated This is deprecated
+     */
     public static abstract class WpsCallback {
-        /** WPS start succeeded */
+
+        /** WPS start succeeded
+         * @deprecated This API is deprecated
+         */
         public abstract void onStarted(String pin);
 
-        /** WPS operation completed successfully */
+        /** WPS operation completed successfully
+         * @deprecated This API is deprecated
+         */
         public abstract void onSucceeded();
 
         /**
@@ -2326,6 +2358,7 @@
          * {@link #WPS_TKIP_ONLY_PROHIBITED}, {@link #WPS_OVERLAP_ERROR},
          * {@link #WPS_WEP_PROHIBITED}, {@link #WPS_TIMED_OUT} or {@link #WPS_AUTH_FAILURE}
          * and some generic errors.
+         * @deprecated This API is deprecated
          */
         public abstract void onFailed(int reason);
     }
@@ -3032,6 +3065,7 @@
      * @param listener for callbacks on success or failure. Can be null.
      * @throws IllegalStateException if the WifiManager instance needs to be
      * initialized again
+     * @deprecated This API is deprecated
      */
     public void startWps(WpsInfo config, WpsCallback listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
@@ -3044,6 +3078,7 @@
      * @param listener for callbacks on success or failure. Can be null.
      * @throws IllegalStateException if the WifiManager instance needs to be
      * initialized again
+     * @deprecated This API is deprecated
      */
     public void cancelWps(WpsCallback listener) {
         getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener));
diff --git a/wifi/java/android/net/wifi/rtt/LocationCivic.java b/wifi/java/android/net/wifi/rtt/LocationCivic.java
new file mode 100644
index 0000000..610edb6
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationCivic.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Location Civic Report (LCR).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCR report.
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationCivic implements Parcelable {
+    private final byte[] mData;
+
+    /**
+     * Parse the raw LCR information element (byte array) and extract the LocationCivic structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationCivic parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationCivic(byte[] data) {
+        mData = data;
+    }
+
+    /**
+     * Return the Location Civic data reported by the peer.
+     *
+     * @return An arbitrary location information.
+     */
+    public byte[] getData() {
+        return mData;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mData);
+    }
+
+    public static final Parcelable.Creator<LocationCivic> CREATOR =
+            new Parcelable.Creator<LocationCivic>() {
+                @Override
+                public LocationCivic[] newArray(int size) {
+                    return new LocationCivic[size];
+                }
+
+                @Override
+                public LocationCivic createFromParcel(Parcel in) {
+                    byte[] data = in.createByteArray();
+
+                    return new LocationCivic(data);
+                }
+            };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationCivic)) {
+            return false;
+        }
+
+        LocationCivic lhs = (LocationCivic) o;
+
+        return Arrays.equals(mData, lhs.mData);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mData);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
new file mode 100644
index 0000000..8aba56a
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The Device Location Configuration Information (LCI) specifies the location information of a peer
+ * device (e.g. an Access Point).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCI report (Location configuration information
+ * report).
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationConfigurationInformation implements Parcelable {
+    /** @hide */
+    @IntDef({
+            ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AltitudeTypes {
+    }
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * does not specify an altitude or altitude uncertainty. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw
+     * an exception.
+     */
+    public static final int ALTITUDE_UNKNOWN = 0;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * specifies the altitude and altitude uncertainty in meters. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters.
+     */
+    public static final int ALTITUDE_IN_METERS = 1;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the
+     * location specifies the altitude in floors, and does not specify an altitude uncertainty.
+     * The {@link #getAltitude()} method returns valid value in floors, and the
+     * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception.
+     */
+    public static final int ALTITUDE_IN_FLOORS = 2;
+
+    private final double mLatitude;
+    private final double mLatitudeUncertainty;
+    private final double mLongitude;
+    private final double mLongitudeUncertainty;
+    private final int mAltitudeType;
+    private final double mAltitude;
+    private final double mAltitudeUncertainty;
+
+    /**
+     * Parse the raw LCI information element (byte array) and extract the
+     * LocationConfigurationInformation structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationConfigurationInformation(double latitude, double latitudeUncertainty,
+            double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType,
+            double altitude, double altitudeUncertainty) {
+        mLatitude = latitude;
+        mLatitudeUncertainty = latitudeUncertainty;
+        mLongitude = longitude;
+        mLongitudeUncertainty = longitudeUncertainty;
+        mAltitudeType = altitudeType;
+        mAltitude = altitude;
+        mAltitudeUncertainty = altitudeUncertainty;
+    }
+
+    /**
+     * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -90 and 90.
+     *
+     * @return Latitude in degrees.
+     */
+    public double getLatitude() {
+        return mLatitude;
+    }
+
+    /**
+     * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates
+     * an unknown uncertainty.
+     *
+     * @return Uncertainty of the latitude in degrees.
+     */
+    public double getLatitudeUncertainty() {
+        return mLatitudeUncertainty;
+    }
+
+    /**
+     * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -180 and 180.
+     *
+     * @return Longitude in degrees.
+     */
+    public double getLongitude() {
+        return mLongitude;
+    }
+
+    /**
+     * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the longitude in degrees.
+     */
+    public double getLongitudeUncertainty() {
+        return mLongitudeUncertainty;
+    }
+
+    /**
+     * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and
+     * {@link #getAltitudeUncertainty()}. The possible values are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in
+     * meters. Values are per WGS 84 reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty
+     * is not provided.
+     *
+     * @return The type of the altitude and altitude uncertainty.
+     */
+    public @AltitudeTypes int getAltitudeType() {
+        return mAltitudeType;
+    }
+
+    /**
+     * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values
+     * are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an
+     * exception.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84
+     * reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors.
+     *
+     * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}.
+     */
+    public double getAltitude() {
+        if (mAltitudeType == ALTITUDE_UNKNOWN) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN");
+        }
+        return mAltitude;
+    }
+
+    /**
+     * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} -
+     * otherwise this method will throw an exception.
+     * <p>
+     * Get the uncertainty of the altitude {@link #getAltitude()} in meters.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the altitude in meters.
+     */
+    public double getAltitudeUncertainty() {
+        if (mAltitudeType != ALTITUDE_IN_METERS) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS");
+        }
+        return mAltitudeUncertainty;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeDouble(mLatitude);
+        dest.writeDouble(mLatitudeUncertainty);
+        dest.writeDouble(mLongitude);
+        dest.writeDouble(mLongitudeUncertainty);
+        dest.writeInt(mAltitudeType);
+        dest.writeDouble(mAltitude);
+        dest.writeDouble(mAltitudeUncertainty);
+    }
+
+    public static final Creator<LocationConfigurationInformation> CREATOR =
+            new Creator<LocationConfigurationInformation>() {
+        @Override
+        public LocationConfigurationInformation[] newArray(int size) {
+            return new LocationConfigurationInformation[size];
+        }
+
+        @Override
+        public LocationConfigurationInformation createFromParcel(Parcel in) {
+            double latitude = in.readDouble();
+            double latitudeUnc = in.readDouble();
+            double longitude = in.readDouble();
+            double longitudeUnc = in.readDouble();
+            int altitudeType = in.readInt();
+            double altitude = in.readDouble();
+            double altitudeUnc = in.readDouble();
+
+            return new LocationConfigurationInformation(latitude, latitudeUnc, longitude,
+                    longitudeUnc, altitudeType, altitude, altitudeUnc);
+        }
+    };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCI: latitude=").append(mLatitude).append(
+                ", latitudeUncertainty=").append(mLatitudeUncertainty).append(
+                ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append(
+                mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append(
+                ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append(
+                mAltitudeUncertainty).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationConfigurationInformation)) {
+            return false;
+        }
+
+        LocationConfigurationInformation lhs = (LocationConfigurationInformation) o;
+
+        return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty
+                && mLongitude == lhs.mLongitude
+                && mLongitudeUncertainty == lhs.mLongitudeUncertainty
+                && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude
+                && mAltitudeUncertainty == lhs.mAltitudeUncertainty;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty,
+                mAltitudeType, mAltitude, mAltitudeUncertainty);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index d5ca8f7..201833b 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -65,29 +65,37 @@
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
     private final int mRssi;
+    private final LocationConfigurationInformation mLci;
+    private final LocationCivic mLcr;
     private final long mTimestamp;
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = mac;
         mPeerHandle = null;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
@@ -169,6 +177,38 @@
     }
 
     /**
+     * @return The Location Configuration Information (LCI) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     */
+    @Nullable
+    public LocationConfigurationInformation getReportedLocationConfigurationInformation() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationConfigurationInformation(): invoked on an invalid result: "
+                            + "getStatus()=" + mStatus);
+        }
+        return mLci;
+    }
+
+    /**
+     * @return The Location Civic report (LCR) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     */
+    @Nullable
+    public LocationCivic getReportedLocationCivic() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationCivic(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mLcr;
+    }
+
+    /**
      * @return The timestamp, in us since boot, at which the ranging operation was performed.
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
@@ -205,6 +245,18 @@
         dest.writeInt(mDistanceMm);
         dest.writeInt(mDistanceStdDevMm);
         dest.writeInt(mRssi);
+        if (mLci == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLci.writeToParcel(dest, flags);
+        }
+        if (mLcr == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLcr.writeToParcel(dest, flags);
+        }
         dest.writeLong(mTimestamp);
     }
 
@@ -230,13 +282,23 @@
             int distanceMm = in.readInt();
             int distanceStdDevMm = in.readInt();
             int rssi = in.readInt();
+            boolean lciPresent = in.readBoolean();
+            LocationConfigurationInformation lci = null;
+            if (lciPresent) {
+                lci = LocationConfigurationInformation.CREATOR.createFromParcel(in);
+            }
+            boolean lcrPresent = in.readBoolean();
+            LocationCivic lcr = null;
+            if (lcrPresent) {
+                lcr = LocationCivic.CREATOR.createFromParcel(in);
+            }
             long timestamp = in.readLong();
             if (peerHandlePresent) {
                 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             } else {
                 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             }
         }
     };
@@ -248,8 +310,8 @@
                 mMac).append(", peerHandle=").append(
                 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
                 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
-                ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
-                "]").toString();
+                ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+                mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
     }
 
     @Override
@@ -267,12 +329,13 @@
         return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
+                && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr)
                 && mTimestamp == lhs.mTimestamp;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
-                mTimestamp);
+                mLci, mLcr, mTimestamp);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 72e95b9..41c7f86 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -32,7 +33,6 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,7 +46,6 @@
 /**
  * Unit test harness for WifiRttManager class.
  */
-@SmallTest
 public class WifiRttManagerTest {
     private WifiRttManager mDut;
     private TestLooper mMockLooper;
@@ -80,7 +79,7 @@
         List<RangingResult> results = new ArrayList<>();
         results.add(
                 new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
-                        10, 666));
+                        10, null, null, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -236,10 +235,23 @@
         int distanceStdDevCm = 10;
         int rssi = 5;
         long timestamp = System.currentTimeMillis();
+        double latitude = 5.5;
+        double latitudeUncertainty = 6.5;
+        double longitude = 7.5;
+        double longitudeUncertainty = 8.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_METERS;
+        double altitude = 9.5;
+        double altitudeUncertainty = 55.5;
+        byte[] lcrData = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC };
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+        LocationCivic lcr = new LocationCivic(lcrData);
 
         // RangingResults constructed with a MAC address
         RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                lci, lcr, timestamp);
 
         Parcel parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -255,7 +267,7 @@
 
         // RangingResults constructed with a PeerHandle
         result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                null, null, timestamp);
 
         parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -269,4 +281,83 @@
 
         assertEquals(result, rereadResult);
     }
+
+    /**
+     * Validate that LocationConfigurationInformation parcel works (produces same object on
+     * write/read).
+     */
+    @Test
+    public void testLciParcel() {
+        double latitude = 1.5;
+        double latitudeUncertainty = 2.5;
+        double longitude = 3.5;
+        double longitudeUncertainty = 4.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_FLOORS;
+        double altitude = 5.5;
+        double altitudeUncertainty = 6.5;
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+
+        Parcel parcelW = Parcel.obtain();
+        lci.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        LocationConfigurationInformation rereadLci =
+                LocationConfigurationInformation.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(lci, rereadLci);
+    }
+
+    /**
+     * Validate that the LCI throws an exception when accessing invalid fields an certain altitude
+     * types.
+     */
+    @Test
+    public void testLciInvalidAltitudeFieldAccess() {
+        boolean exceptionThrown;
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_UNKNOWN, 0, 0);
+
+        // UNKNOWN - invalid altitude & altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitude();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitude()", exceptionThrown);
+
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitudeUncertainty()", exceptionThrown);
+
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_FLOORS, 0, 0);
+
+        // FLOORS - invalid altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("FLOORS / getAltitudeUncertainty()", exceptionThrown);
+
+        // and good accesses just in case
+        lci.getAltitude();
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_METERS, 0, 0);
+        lci.getAltitude();
+        lci.getAltitudeUncertainty();
+    }
 }