Merge "Change null behavior of getCellBandwidths" into pi-dev
diff --git a/Android.bp b/Android.bp
index 1caa497..be83210 100644
--- a/Android.bp
+++ b/Android.bp
@@ -169,8 +169,6 @@
         "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl",
-        "core/java/android/hardware/location/IFusedLocationHardware.aidl",
-        "core/java/android/hardware/location/IFusedLocationHardwareSink.aidl",
         "core/java/android/hardware/location/IGeofenceHardware.aidl",
         "core/java/android/hardware/location/IGeofenceHardwareCallback.aidl",
         "core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl",
@@ -409,7 +407,6 @@
         "location/java/android/location/IBatchedLocationCallback.aidl",
         "location/java/android/location/ICountryDetector.aidl",
         "location/java/android/location/ICountryListener.aidl",
-        "location/java/android/location/IFusedProvider.aidl",
         "location/java/android/location/IGeocodeProvider.aidl",
         "location/java/android/location/IGeofenceProvider.aidl",
         "location/java/android/location/IGnssStatusListener.aidl",
@@ -462,6 +459,8 @@
         "media/java/android/media/session/ISessionController.aidl",
         "media/java/android/media/session/ISessionControllerCallback.aidl",
         "media/java/android/media/session/ISessionManager.aidl",
+        "media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl",
+        "media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl",
         "media/java/android/media/tv/ITvInputClient.aidl",
         "media/java/android/media/tv/ITvInputHardware.aidl",
         "media/java/android/media/tv/ITvInputHardwareCallback.aidl",
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
new file mode 100644
index 0000000..28d4096
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BinderCallsStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertNull;
+
+
+/**
+ * Performance tests for {@link BinderCallsStats}
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BinderCallsStatsPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    private BinderCallsStats mBinderCallsStats;
+
+    @Before
+    public void setUp() {
+        mBinderCallsStats = new BinderCallsStats(true);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void timeCallSession() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        int i = 0;
+        while (state.keepRunning()) {
+            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, i % 100);
+            mBinderCallsStats.callEnded(s);
+            i++;
+        }
+    }
+
+    @Test
+    public void timeCallSessionTrackingDisabled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        mBinderCallsStats = new BinderCallsStats(false);
+        assertNull(mBinderCallsStats.callStarted(b, 0));
+        while (state.keepRunning()) {
+            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
+            mBinderCallsStats.callEnded(s);
+        }
+    }
+
+}
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index 73e1724..ccbccca 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -119,8 +119,12 @@
         // Report median of randomly generated PrecomputedText.
         for (int i = 0; i < TRIAL_COUNT; ++i) {
             CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
     }
@@ -136,8 +140,12 @@
         // Report median of randomly generated PrecomputedText.
         for (int i = 0; i < TRIAL_COUNT; ++i) {
             CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
     }
diff --git a/api/current.txt b/api/current.txt
index 69d9a9e..dc3ab4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1378,7 +1378,7 @@
     field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
     field public static final int textEditSuggestionItemLayout = 16843636; // 0x1010374
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
-    field public static final int textFontWeight = 16844166; // 0x1010586
+    field public static final int textFontWeight = 16844165; // 0x1010585
     field public static final int textIsSelectable = 16843542; // 0x1010316
     field public static final int textOff = 16843045; // 0x1010125
     field public static final int textOn = 16843044; // 0x1010124
@@ -1469,7 +1469,6 @@
     field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
-    field public static final int urlBarResourceId = 16844164; // 0x1010584
     field public static final int use32bitAbi = 16844053; // 0x1010515
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
@@ -1549,7 +1548,7 @@
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
-    field public static final int windowLayoutInDisplayCutoutMode = 16844167; // 0x1010587
+    field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
     field public static final int windowLightNavigationBar = 16844140; // 0x101056c
     field public static final int windowLightStatusBar = 16844000; // 0x10104e0
     field public static final int windowMinWidthMajor = 16843606; // 0x1010356
@@ -5401,6 +5400,7 @@
     method public android.app.Notification.Builder extend(android.app.Notification.Extender);
     method public android.os.Bundle getExtras();
     method public deprecated android.app.Notification getNotification();
+    method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -6558,7 +6558,7 @@
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
     method public void setLogoutEnabled(android.content.ComponentName, boolean);
     method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
-    method public void setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
+    method public boolean setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -7192,6 +7192,7 @@
     field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
     field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     field public static final java.lang.String HINT_ACTIONS = "actions";
+    field public static final java.lang.String HINT_ERROR = "error";
     field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
     field public static final java.lang.String HINT_KEY_WORDS = "key_words";
     field public static final java.lang.String HINT_LARGE = "large";
@@ -7217,29 +7218,22 @@
   }
 
   public static class Slice.Builder {
-    ctor public Slice.Builder(android.net.Uri);
+    ctor public deprecated Slice.Builder(android.net.Uri);
+    ctor public Slice.Builder(android.net.Uri, android.app.slice.SliceSpec);
     ctor public Slice.Builder(android.app.slice.Slice.Builder);
-    method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
-    method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.util.List<java.lang.String>);
+    method public android.app.slice.Slice.Builder addLong(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
-    method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice);
     method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String);
-    method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
-    method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
+    method public deprecated android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice build();
     method public android.app.slice.Slice.Builder setCallerNeeded(boolean);
-    method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
+    method public deprecated android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
   }
 
   public final class SliceItem implements android.os.Parcelable {
@@ -7262,10 +7256,11 @@
     field public static final java.lang.String FORMAT_BUNDLE = "bundle";
     field public static final java.lang.String FORMAT_IMAGE = "image";
     field public static final java.lang.String FORMAT_INT = "int";
+    field public static final java.lang.String FORMAT_LONG = "long";
     field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
     field public static final java.lang.String FORMAT_SLICE = "slice";
     field public static final java.lang.String FORMAT_TEXT = "text";
-    field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
+    field public static final deprecated java.lang.String FORMAT_TIMESTAMP = "long";
   }
 
   public class SliceManager {
@@ -7281,6 +7276,13 @@
     field public static final java.lang.String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
   }
 
+  public class SliceMetrics {
+    ctor public SliceMetrics(android.content.Context, android.net.Uri);
+    method public void logHidden();
+    method public void logTouch(android.net.Uri);
+    method public void logVisible();
+  }
+
   public abstract class SliceProvider extends android.content.ContentProvider {
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -11164,6 +11166,7 @@
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+    method public android.os.PersistableBundle getSuspendedPackageAppExtras();
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -11177,6 +11180,7 @@
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
     method public abstract boolean isInstantApp(java.lang.String);
+    method public boolean isPackageSuspended();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -13610,7 +13614,9 @@
 
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public void close();
+    method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, int);
     method public static android.graphics.ImageDecoder.Source createSource(android.content.ContentResolver, android.net.Uri);
+    method public static android.graphics.ImageDecoder.Source createSource(android.content.res.AssetManager, java.lang.String);
     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;
@@ -13640,12 +13646,14 @@
     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 final class ImageDecoder.DecodeException extends java.io.IOException {
+    method public int getError();
+    method public android.graphics.ImageDecoder.Source getSource();
+    field public static final int SOURCE_EXCEPTION = 1; // 0x1
+    field public static final int SOURCE_INCOMPLETE = 2; // 0x2
+    field public static final int SOURCE_MALFORMED_DATA = 3; // 0x3
   }
 
   public static class ImageDecoder.ImageInfo {
@@ -13654,16 +13662,12 @@
     method public boolean isAnimated();
   }
 
-  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);
+    method public abstract boolean onPartialImage(android.graphics.ImageDecoder.DecodeException);
   }
 
   public static abstract class ImageDecoder.Source {
@@ -15776,7 +15780,7 @@
     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();
+    method public java.util.Set<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;
@@ -15802,6 +15806,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES;
@@ -15814,7 +15819,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE;
     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 deprecated 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;
@@ -16272,6 +16277,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> JPEG_THUMBNAIL_QUALITY;
     field public static final android.hardware.camera2.CaptureResult.Key<android.util.Size> JPEG_THUMBNAIL_SIZE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FILTER_DENSITY;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCAL_LENGTH;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
@@ -16280,7 +16286,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION;
-    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
+    field public static final deprecated android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
@@ -16443,8 +16449,8 @@
   }
 
   public final class SessionConfiguration {
-    ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler);
-    method public android.os.Handler getHandler();
+    ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
+    method public java.util.concurrent.Executor getExecutor();
     method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
     method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
     method public android.hardware.camera2.CaptureRequest getSessionParameters();
@@ -21539,10 +21545,10 @@
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
     method public double getAutomaticGainControlLevelDb();
-    method public long getCarrierCycles();
+    method public deprecated long getCarrierCycles();
     method public float getCarrierFrequencyHz();
-    method public double getCarrierPhase();
-    method public double getCarrierPhaseUncertainty();
+    method public deprecated double getCarrierPhase();
+    method public deprecated double getCarrierPhaseUncertainty();
     method public double getCn0DbHz();
     method public int getConstellationType();
     method public int getMultipathIndicator();
@@ -21555,13 +21561,15 @@
     method public int getSvid();
     method public double getTimeOffsetNanos();
     method public boolean hasAutomaticGainControlLevelDb();
-    method public boolean hasCarrierCycles();
+    method public deprecated boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
-    method public boolean hasCarrierPhase();
-    method public boolean hasCarrierPhaseUncertainty();
+    method public deprecated boolean hasCarrierPhase();
+    method public deprecated boolean hasCarrierPhaseUncertainty();
     method public boolean hasSnrInDb();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
+    field public static final int ADR_STATE_HALF_CYCLE_REPORTED = 16; // 0x10
+    field public static final int ADR_STATE_HALF_CYCLE_RESOLVED = 8; // 0x8
     field public static final int ADR_STATE_RESET = 2; // 0x2
     field public static final int ADR_STATE_UNKNOWN = 0; // 0x0
     field public static final int ADR_STATE_VALID = 1; // 0x1
@@ -21811,7 +21819,6 @@
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
-    field public static final java.lang.String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -22084,7 +22091,6 @@
     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();
@@ -22410,7 +22416,6 @@
     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);
@@ -22425,7 +22430,6 @@
     method public int setPresentation(android.media.AudioPresentation);
     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);
@@ -22461,7 +22465,6 @@
     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;
@@ -22485,12 +22488,6 @@
     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);
@@ -22543,40 +22540,6 @@
     field public static final int QUALITY_MEDIUM = 1; // 0x1
   }
 
-  public final class DataSourceDesc {
-    method public long getEndPosition();
-    method public java.io.FileDescriptor getFileDescriptor();
-    method public long getFileDescriptorLength();
-    method public long getFileDescriptorOffset();
-    method public android.media.Media2DataSource getMedia2DataSource();
-    method public java.lang.String getMediaId();
-    method public long getStartPosition();
-    method public int getType();
-    method public android.net.Uri getUri();
-    method public android.content.Context getUriContext();
-    method public java.util.List<java.net.HttpCookie> getUriCookies();
-    method public java.util.Map<java.lang.String, java.lang.String> getUriHeaders();
-    field public static final long LONG_MAX = 576460752303423487L; // 0x7ffffffffffffffL
-    field public static final int TYPE_CALLBACK = 1; // 0x1
-    field public static final int TYPE_FD = 2; // 0x2
-    field public static final int TYPE_NONE = 0; // 0x0
-    field public static final int TYPE_URI = 3; // 0x3
-  }
-
-  public static class DataSourceDesc.Builder {
-    ctor public DataSourceDesc.Builder();
-    ctor public DataSourceDesc.Builder(android.media.DataSourceDesc);
-    method public android.media.DataSourceDesc build();
-    method public android.media.DataSourceDesc.Builder setDataSource(android.media.Media2DataSource);
-    method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor);
-    method public android.media.DataSourceDesc.Builder setDataSource(java.io.FileDescriptor, long, long);
-    method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri);
-    method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>);
-    method public android.media.DataSourceDesc.Builder setEndPosition(long);
-    method public android.media.DataSourceDesc.Builder setMediaId(java.lang.String);
-    method public android.media.DataSourceDesc.Builder setStartPosition(long);
-  }
-
   public final class DeniedByServerException extends android.media.MediaDrmException {
     ctor public DeniedByServerException(java.lang.String);
   }
@@ -22854,12 +22817,6 @@
     method public abstract void onJetUserIdUpdate(android.media.JetPlayer, int, int);
   }
 
-  public abstract class Media2DataSource implements java.io.Closeable {
-    ctor public Media2DataSource();
-    method public abstract long getSize() throws java.io.IOException;
-    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
-  }
-
   public class MediaActionSound {
     ctor public MediaActionSound();
     method public void load(int);
@@ -22871,27 +22828,6 @@
     field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
   }
 
-  public class MediaBrowser2 extends android.media.MediaController2 {
-    ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback);
-    method public void getChildren(java.lang.String, int, int, android.os.Bundle);
-    method public void getItem(java.lang.String);
-    method public void getLibraryRoot(android.os.Bundle);
-    method public void getSearchResult(java.lang.String, int, int, android.os.Bundle);
-    method public void search(java.lang.String, android.os.Bundle);
-    method public void subscribe(java.lang.String, android.os.Bundle);
-    method public void unsubscribe(java.lang.String);
-  }
-
-  public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback {
-    ctor public MediaBrowser2.BrowserCallback();
-    method public void onChildrenChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
-    method public void onGetChildrenDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
-    method public void onGetItemDone(android.media.MediaBrowser2, java.lang.String, android.media.MediaItem2);
-    method public void onGetLibraryRootDone(android.media.MediaBrowser2, android.os.Bundle, java.lang.String, android.os.Bundle);
-    method public void onGetSearchResultDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List<android.media.MediaItem2>, android.os.Bundle);
-    method public void onSearchResultChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle);
-  }
-
   public final class MediaCas implements java.lang.AutoCloseable {
     ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
     method public void close();
@@ -23369,82 +23305,6 @@
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
 
-  public class MediaController2 implements java.lang.AutoCloseable {
-    ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
-    method public void addPlaylistItem(int, android.media.MediaItem2);
-    method public void adjustVolume(int, int);
-    method public void close();
-    method public void fastForward();
-    method public long getBufferedPosition();
-    method public android.media.MediaItem2 getCurrentMediaItem();
-    method public android.media.MediaController2.PlaybackInfo getPlaybackInfo();
-    method public float getPlaybackSpeed();
-    method public int getPlayerState();
-    method public java.util.List<android.media.MediaItem2> getPlaylist();
-    method public android.media.MediaMetadata2 getPlaylistMetadata();
-    method public long getPosition();
-    method public int getRepeatMode();
-    method public android.app.PendingIntent getSessionActivity();
-    method public android.media.SessionToken2 getSessionToken();
-    method public int getShuffleMode();
-    method public boolean isConnected();
-    method public void pause();
-    method public void play();
-    method public void playFromMediaId(java.lang.String, android.os.Bundle);
-    method public void playFromSearch(java.lang.String, android.os.Bundle);
-    method public void playFromUri(android.net.Uri, android.os.Bundle);
-    method public void prepare();
-    method public void prepareFromMediaId(java.lang.String, android.os.Bundle);
-    method public void prepareFromSearch(java.lang.String, android.os.Bundle);
-    method public void prepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void removePlaylistItem(android.media.MediaItem2);
-    method public void replacePlaylistItem(int, android.media.MediaItem2);
-    method public void rewind();
-    method public void seekTo(long);
-    method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
-    method public void setPlaybackSpeed(float);
-    method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void setRating(java.lang.String, android.media.Rating2);
-    method public void setRepeatMode(int);
-    method public void setShuffleMode(int);
-    method public void setVolumeTo(int, int);
-    method public void skipToNextItem();
-    method public void skipToPlaylistItem(android.media.MediaItem2);
-    method public void skipToPreviousItem();
-    method public void stop();
-    method public void updatePlaylistMetadata(android.media.MediaMetadata2);
-  }
-
-  public static abstract class MediaController2.ControllerCallback {
-    ctor public MediaController2.ControllerCallback();
-    method public void onAllowedCommandsChanged(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
-    method public void onBufferedPositionChanged(android.media.MediaController2, long);
-    method public void onConnected(android.media.MediaController2, android.media.MediaSession2.CommandGroup);
-    method public void onCurrentMediaItemChanged(android.media.MediaController2, android.media.MediaItem2);
-    method public void onCustomCommand(android.media.MediaController2, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
-    method public void onCustomLayoutChanged(android.media.MediaController2, java.util.List<android.media.MediaSession2.CommandButton>);
-    method public void onDisconnected(android.media.MediaController2);
-    method public void onError(android.media.MediaController2, int, android.os.Bundle);
-    method public void onPlaybackInfoChanged(android.media.MediaController2, android.media.MediaController2.PlaybackInfo);
-    method public void onPlaybackSpeedChanged(android.media.MediaController2, float);
-    method public void onPlayerStateChanged(android.media.MediaController2, int);
-    method public void onPlaylistChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void onPlaylistMetadataChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
-    method public void onPositionChanged(android.media.MediaController2, long, long);
-    method public void onRepeatModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
-    method public void onShuffleModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int);
-  }
-
-  public static final class MediaController2.PlaybackInfo {
-    method public android.media.AudioAttributes getAudioAttributes();
-    method public int getControlType();
-    method public int getCurrentVolume();
-    method public int getMaxVolume();
-    method public int getPlaybackType();
-    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
-    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
-  }
-
   public final class MediaCrypto {
     ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
     method protected void finalize();
@@ -23781,10 +23641,8 @@
     field public static final java.lang.String KEY_DURATION = "durationUs";
     field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
     field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
-    field public static final java.lang.String KEY_GRID_COLS = "grid-cols";
-    field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height";
+    field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols";
     field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
-    field public static final java.lang.String KEY_GRID_WIDTH = "grid-width";
     field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
     field public static final java.lang.String KEY_HEIGHT = "height";
     field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -23813,6 +23671,8 @@
     field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
     field public static final java.lang.String KEY_STRIDE = "stride";
     field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
+    field public static final java.lang.String KEY_TILE_HEIGHT = "tile-height";
+    field public static final java.lang.String KEY_TILE_WIDTH = "tile-width";
     field public static final java.lang.String KEY_TRACK_ID = "track-id";
     field public static final java.lang.String KEY_WIDTH = "width";
     field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
@@ -23845,71 +23705,6 @@
     field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
   }
 
-  public class MediaItem2 {
-    method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle);
-    method public android.media.DataSourceDesc getDataSourceDesc();
-    method public int getFlags();
-    method public java.lang.String getMediaId();
-    method public android.media.MediaMetadata2 getMetadata();
-    method public boolean isBrowsable();
-    method public boolean isPlayable();
-    method public void setMetadata(android.media.MediaMetadata2);
-    method public android.os.Bundle toBundle();
-    field public static final int FLAG_BROWSABLE = 1; // 0x1
-    field public static final int FLAG_PLAYABLE = 2; // 0x2
-  }
-
-  public static final class MediaItem2.Builder {
-    ctor public MediaItem2.Builder(android.content.Context, int);
-    method public android.media.MediaItem2 build();
-    method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc);
-    method public android.media.MediaItem2.Builder setMediaId(java.lang.String);
-    method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2);
-  }
-
-  public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 {
-    ctor public MediaLibraryService2();
-    method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
-  }
-
-  public static final class MediaLibraryService2.LibraryRoot {
-    ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle);
-    method public android.os.Bundle getExtras();
-    method public java.lang.String getRootId();
-    field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
-    field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT";
-    field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
-  }
-
-  public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 {
-    method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
-    method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle);
-    method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle);
-  }
-
-  public static final class MediaLibraryService2.MediaLibrarySession.Builder {
-    ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
-    method public android.media.MediaLibraryService2.MediaLibrarySession build();
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlayer(android.media.MediaPlayerBase);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback);
-    method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2);
-  }
-
-  public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback {
-    ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context);
-    method public java.util.List<android.media.MediaItem2> onGetChildren(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
-    method public android.media.MediaItem2 onGetItem(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
-    method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, android.os.Bundle);
-    method public java.util.List<android.media.MediaItem2> onGetSearchResult(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle);
-    method public void onSearch(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onSubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onUnsubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String);
-  }
-
   public final class MediaMetadata implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -23965,79 +23760,6 @@
     method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
   }
 
-  public final class MediaMetadata2 {
-    method public boolean containsKey(java.lang.String);
-    method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle);
-    method public android.graphics.Bitmap getBitmap(java.lang.String);
-    method public android.os.Bundle getExtras();
-    method public float getFloat(java.lang.String);
-    method public long getLong(java.lang.String);
-    method public java.lang.String getMediaId();
-    method public android.media.Rating2 getRating(java.lang.String);
-    method public java.lang.String getString(java.lang.String);
-    method public java.lang.CharSequence getText(java.lang.String);
-    method public java.util.Set<java.lang.String> keySet();
-    method public int size();
-    method public android.os.Bundle toBundle();
-    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
-    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
-    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
-    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
-    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
-    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
-    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
-    field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
-    field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-    field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
-    field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
-    field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
-    field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
-    field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
-    field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
-    field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
-    field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
-    field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
-    field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
-    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
-    field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
-    field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
-    field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
-    field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
-    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
-    field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
-    field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
-    field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN";
-    field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY";
-    field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
-    field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
-    field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-    field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
-    field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
-    field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
-    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
-    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
-    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
-  }
-
-  public static final class MediaMetadata2.Builder {
-    ctor public MediaMetadata2.Builder(android.content.Context);
-    ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2);
-    method public android.media.MediaMetadata2 build();
-    method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
-    method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float);
-    method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long);
-    method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2);
-    method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String);
-    method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence);
-    method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle);
-  }
-
   public abstract deprecated class MediaMetadataEditor {
     method public synchronized void addEditableKey(int);
     method public abstract void apply();
@@ -24062,12 +23784,16 @@
     method public java.lang.String extractMetadata(int);
     method public byte[] getEmbeddedPicture();
     method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getFrameAtIndex(int);
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
     method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int);
     method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getImageAtIndex(int);
     method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getPrimaryImage();
     method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -24352,263 +24078,6 @@
     field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
   }
 
-  public abstract class MediaPlayer2 extends android.media.MediaPlayerBase implements android.media.AudioRouting {
-    method public abstract void attachAuxEffect(int);
-    method public abstract void clearDrmEventCallback();
-    method public abstract void clearMediaPlayer2EventCallback();
-    method public abstract void clearPendingCommands();
-    method public abstract void close();
-    method public static final android.media.MediaPlayer2 create();
-    method public abstract void deselectTrack(int);
-    method public abstract int getAudioSessionId();
-    method public abstract long getBufferedPosition();
-    method public abstract long getCurrentPosition();
-    method public abstract android.media.MediaPlayer2.DrmInfo getDrmInfo();
-    method public abstract android.media.MediaDrm.KeyRequest getDrmKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public abstract long getDuration();
-    method public abstract android.os.PersistableBundle getMetrics();
-    method public abstract android.media.PlaybackParams getPlaybackParams();
-    method public abstract int getSelectedTrack(int);
-    method public abstract android.media.SyncParams getSyncParams();
-    method public abstract android.media.MediaTimestamp getTimestamp();
-    method public abstract java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
-    method public abstract int getVideoHeight();
-    method public abstract int getVideoWidth();
-    method public void notifyWhenCommandLabelReached(java.lang.Object);
-    method public abstract void prepareDrm(java.util.UUID) throws android.media.MediaPlayer2.ProvisioningNetworkErrorException, android.media.MediaPlayer2.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
-    method public abstract byte[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
-    method public abstract void releaseDrm() throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public abstract void restoreDrmKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public void seekTo(long);
-    method public abstract void seekTo(long, int);
-    method public abstract void selectTrack(int);
-    method public abstract void setAudioSessionId(int);
-    method public abstract void setAuxEffectSendLevel(float);
-    method public abstract void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
-    method public abstract void setDrmPropertyString(java.lang.String, java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public abstract void setMediaPlayer2EventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.MediaPlayer2EventCallback);
-    method public abstract void setOnDrmConfigHelper(android.media.MediaPlayer2.OnDrmConfigHelper);
-    method public abstract void setPlaybackParams(android.media.PlaybackParams);
-    method public abstract void setSurface(android.view.Surface);
-    method public abstract void setSyncParams(android.media.SyncParams);
-    field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
-    field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
-    field public static final int CALL_COMPLETED_LOOP_CURRENT = 3; // 0x3
-    field public static final int CALL_COMPLETED_PAUSE = 4; // 0x4
-    field public static final int CALL_COMPLETED_PLAY = 5; // 0x5
-    field public static final int CALL_COMPLETED_PREPARE = 6; // 0x6
-    field public static final int CALL_COMPLETED_RELEASE_DRM = 12; // 0xc
-    field public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13; // 0xd
-    field public static final int CALL_COMPLETED_SEEK_TO = 14; // 0xe
-    field public static final int CALL_COMPLETED_SELECT_TRACK = 15; // 0xf
-    field public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16; // 0x10
-    field public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17; // 0x11
-    field public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
-    field public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19; // 0x13
-    field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22; // 0x16
-    field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23; // 0x17
-    field public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24; // 0x18
-    field public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25; // 0x19
-    field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
-    field public static final int CALL_COMPLETED_SET_SURFACE = 27; // 0x1b
-    field public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28; // 0x1c
-    field public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29; // 0x1d
-    field public static final int CALL_STATUS_BAD_VALUE = 2; // 0x2
-    field public static final int CALL_STATUS_ERROR_IO = 4; // 0x4
-    field public static final int CALL_STATUS_ERROR_UNKNOWN = -2147483648; // 0x80000000
-    field public static final int CALL_STATUS_INVALID_OPERATION = 1; // 0x1
-    field public static final int CALL_STATUS_NO_DRM_SCHEME = 5; // 0x5
-    field public static final int CALL_STATUS_NO_ERROR = 0; // 0x0
-    field public static final int CALL_STATUS_PERMISSION_DENIED = 3; // 0x3
-    field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
-    field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
-    field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
-    field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
-    field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
-    field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
-    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
-    field public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4; // 0x4
-    field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
-    field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
-    field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
-    field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
-    field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
-    field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
-    field public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5; // 0x5
-    field public static final int MEDIA_INFO_PLAYLIST_END = 6; // 0x6
-    field public static final int MEDIA_INFO_PREPARED = 100; // 0x64
-    field public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; // 0x2
-    field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
-    field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
-    field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
-    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
-    field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
-    field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
-    field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
-    field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
-    field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
-    field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
-    field public static final int SEEK_CLOSEST = 3; // 0x3
-    field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
-    field public static final int SEEK_NEXT_SYNC = 1; // 0x1
-    field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
-    field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
-  }
-
-  public static abstract class MediaPlayer2.DrmEventCallback {
-    ctor public MediaPlayer2.DrmEventCallback();
-    method public void onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
-    method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int);
-  }
-
-  public static abstract class MediaPlayer2.DrmInfo {
-    ctor public MediaPlayer2.DrmInfo();
-    method public abstract java.util.Map<java.util.UUID, byte[]> getPssh();
-    method public abstract java.util.List<java.util.UUID> getSupportedSchemes();
-  }
-
-  public static abstract class MediaPlayer2.MediaPlayer2EventCallback {
-    ctor public MediaPlayer2.MediaPlayer2EventCallback();
-    method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
-    method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object);
-    method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
-    method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
-    method public void onMediaTimeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp);
-    method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData);
-    method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
-  }
-
-  public static final class MediaPlayer2.MetricsConstants {
-    field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
-    field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
-    field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
-    field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
-    field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
-    field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
-    field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
-    field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
-    field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
-    field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
-    field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
-    field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
-  }
-
-  public static abstract class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
-    ctor protected MediaPlayer2.NoDrmSchemeException(java.lang.String);
-  }
-
-  public static abstract interface MediaPlayer2.OnDrmConfigHelper {
-    method public abstract void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc);
-  }
-
-  public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
-    ctor protected MediaPlayer2.ProvisioningNetworkErrorException(java.lang.String);
-  }
-
-  public static abstract class MediaPlayer2.ProvisioningServerErrorException extends android.media.MediaDrmException {
-    ctor protected MediaPlayer2.ProvisioningServerErrorException(java.lang.String);
-  }
-
-  public static abstract class MediaPlayer2.TrackInfo {
-    ctor public MediaPlayer2.TrackInfo();
-    method public abstract android.media.MediaFormat getFormat();
-    method public abstract java.lang.String getLanguage();
-    method public abstract int getTrackType();
-    method public abstract java.lang.String toString();
-    field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
-    field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
-    field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
-    field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
-    field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
-  }
-
-  public abstract class MediaPlayerBase implements java.lang.AutoCloseable {
-    ctor public MediaPlayerBase();
-    method public abstract android.media.AudioAttributes getAudioAttributes();
-    method public long getBufferedPosition();
-    method public abstract int getBufferingState();
-    method public abstract android.media.DataSourceDesc getCurrentDataSource();
-    method public long getCurrentPosition();
-    method public long getDuration();
-    method public float getMaxPlayerVolume();
-    method public float getPlaybackSpeed();
-    method public abstract int getPlayerState();
-    method public abstract float getPlayerVolume();
-    method public boolean isReversePlaybackSupported();
-    method public abstract void loopCurrent(boolean);
-    method public abstract void pause();
-    method public abstract void play();
-    method public abstract void prepare();
-    method public abstract void registerPlayerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayerBase.PlayerEventCallback);
-    method public abstract void reset();
-    method public abstract void seekTo(long);
-    method public abstract void setAudioAttributes(android.media.AudioAttributes);
-    method public abstract void setDataSource(android.media.DataSourceDesc);
-    method public abstract void setNextDataSource(android.media.DataSourceDesc);
-    method public abstract void setNextDataSources(java.util.List<android.media.DataSourceDesc>);
-    method public abstract void setPlaybackSpeed(float);
-    method public abstract void setPlayerVolume(float);
-    method public abstract void skipToNext();
-    method public abstract void unregisterPlayerEventCallback(android.media.MediaPlayerBase.PlayerEventCallback);
-    field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
-    field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
-    field public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3; // 0x3
-    field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
-    field public static final int PLAYER_STATE_ERROR = 3; // 0x3
-    field public static final int PLAYER_STATE_IDLE = 0; // 0x0
-    field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
-    field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
-    field public static final long UNKNOWN_TIME = -1L; // 0xffffffffffffffffL
-  }
-
-  public static abstract class MediaPlayerBase.PlayerEventCallback {
-    ctor public MediaPlayerBase.PlayerEventCallback();
-    method public void onBufferingStateChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc, int);
-    method public void onCurrentDataSourceChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc);
-    method public void onMediaPrepared(android.media.MediaPlayerBase, android.media.DataSourceDesc);
-    method public void onPlayerStateChanged(android.media.MediaPlayerBase, int);
-  }
-
-  public abstract class MediaPlaylistAgent {
-    ctor public MediaPlaylistAgent(android.content.Context);
-    method public void addPlaylistItem(int, android.media.MediaItem2);
-    method public java.util.List<android.media.MediaItem2> getPlaylist();
-    method public android.media.MediaMetadata2 getPlaylistMetadata();
-    method public int getRepeatMode();
-    method public int getShuffleMode();
-    method public final void notifyPlaylistChanged();
-    method public final void notifyPlaylistMetadataChanged();
-    method public final void notifyRepeatModeChanged();
-    method public final void notifyShuffleModeChanged();
-    method public final void registerPlaylistEventCallback(java.util.concurrent.Executor, android.media.MediaPlaylistAgent.PlaylistEventCallback);
-    method public void removePlaylistItem(android.media.MediaItem2);
-    method public void replacePlaylistItem(int, android.media.MediaItem2);
-    method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void setRepeatMode(int);
-    method public void setShuffleMode(int);
-    method public void skipToNextItem();
-    method public void skipToPlaylistItem(android.media.MediaItem2);
-    method public void skipToPreviousItem();
-    method public final void unregisterPlaylistEventCallback(android.media.MediaPlaylistAgent.PlaylistEventCallback);
-    method public void updatePlaylistMetadata(android.media.MediaMetadata2);
-    field public static final int REPEAT_MODE_ALL = 2; // 0x2
-    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
-    field public static final int REPEAT_MODE_NONE = 0; // 0x0
-    field public static final int REPEAT_MODE_ONE = 1; // 0x1
-    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
-    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
-    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
-  }
-
-  public static abstract class MediaPlaylistAgent.PlaylistEventCallback {
-    ctor public MediaPlaylistAgent.PlaylistEventCallback();
-    method public void onPlaylistChanged(android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void onPlaylistMetadataChanged(android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
-    method public void onRepeatModeChanged(android.media.MediaPlaylistAgent, int);
-    method public void onShuffleModeChanged(android.media.MediaPlaylistAgent, int);
-  }
-
   public class MediaRecorder implements android.media.AudioRouting {
     ctor public MediaRecorder();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -24885,187 +24354,6 @@
     method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
   }
 
-  public class MediaSession2 implements java.lang.AutoCloseable {
-    method public void addPlaylistItem(int, android.media.MediaItem2);
-    method public void clearOnDataSourceMissingHelper();
-    method public void close();
-    method public void fastForward();
-    method public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
-    method public android.media.MediaItem2 getCurrentMediaItem();
-    method public float getPlaybackSpeed();
-    method public android.media.MediaPlayerBase getPlayer();
-    method public java.util.List<android.media.MediaItem2> getPlaylist();
-    method public android.media.MediaPlaylistAgent getPlaylistAgent();
-    method public android.media.MediaMetadata2 getPlaylistMetadata();
-    method public int getRepeatMode();
-    method public int getShuffleMode();
-    method public android.media.SessionToken2 getToken();
-    method public android.media.VolumeProvider2 getVolumeProvider();
-    method public void notifyError(int, android.os.Bundle);
-    method public void pause();
-    method public void play();
-    method public void prepare();
-    method public void removePlaylistItem(android.media.MediaItem2);
-    method public void replacePlaylistItem(int, android.media.MediaItem2);
-    method public void rewind();
-    method public void seekTo(long);
-    method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle);
-    method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
-    method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup);
-    method public void setAudioFocusRequest(android.media.AudioFocusRequest);
-    method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List<android.media.MediaSession2.CommandButton>);
-    method public void setOnDataSourceMissingHelper(android.media.MediaSession2.OnDataSourceMissingHelper);
-    method public void setPlaybackSpeed(float);
-    method public void setPlaylist(java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void setRepeatMode(int);
-    method public void setShuffleMode(int);
-    method public void skipToNextItem();
-    method public void skipToPlaylistItem(android.media.MediaItem2);
-    method public void skipToPreviousItem();
-    method public void stop();
-    method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistAgent, android.media.VolumeProvider2);
-    method public void updatePlaylistMetadata(android.media.MediaMetadata2);
-    field public static final int COMMAND_CODE_BROWSER = 28; // 0x1c
-    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
-    field public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11; // 0xb
-    field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7
-    field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2
-    field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1
-    field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6
-    field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8
-    field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9
-    field public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10; // 0xa
-    field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4
-    field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5
-    field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3
-    field public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15; // 0xf
-    field public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18; // 0x12
-    field public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20; // 0x14
-    field public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16; // 0x10
-    field public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17; // 0x11
-    field public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19; // 0x13
-    field public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21; // 0x15
-    field public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14; // 0xe
-    field public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13; // 0xd
-    field public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12; // 0xc
-    field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 22; // 0x16
-    field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 24; // 0x18
-    field public static final int COMMAND_CODE_PLAY_FROM_URI = 23; // 0x17
-    field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 25; // 0x19
-    field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 27; // 0x1b
-    field public static final int COMMAND_CODE_PREPARE_FROM_URI = 26; // 0x1a
-    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
-    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
-    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
-    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
-    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
-    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
-    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
-    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
-    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
-    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
-    field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc
-    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
-    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
-  }
-
-  public static final class MediaSession2.Builder {
-    ctor public MediaSession2.Builder(android.content.Context);
-    method public android.media.MediaSession2 build();
-    method public android.media.MediaSession2.Builder setId(java.lang.String);
-    method public android.media.MediaSession2.Builder setPlayer(android.media.MediaPlayerBase);
-    method public android.media.MediaSession2.Builder setPlaylistAgent(android.media.MediaPlaylistAgent);
-    method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
-    method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
-    method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2);
-  }
-
-  public static final class MediaSession2.Command {
-    ctor public MediaSession2.Command(android.content.Context, int);
-    ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle);
-    method public int getCommandCode();
-    method public java.lang.String getCustomCommand();
-    method public android.os.Bundle getExtras();
-  }
-
-  public static final class MediaSession2.CommandButton {
-    method public android.media.MediaSession2.Command getCommand();
-    method public java.lang.String getDisplayName();
-    method public android.os.Bundle getExtras();
-    method public int getIconResId();
-    method public boolean isEnabled();
-  }
-
-  public static final class MediaSession2.CommandButton.Builder {
-    ctor public MediaSession2.CommandButton.Builder(android.content.Context);
-    method public android.media.MediaSession2.CommandButton build();
-    method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command);
-    method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String);
-    method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean);
-    method public android.media.MediaSession2.CommandButton.Builder setExtras(android.os.Bundle);
-    method public android.media.MediaSession2.CommandButton.Builder setIconResId(int);
-  }
-
-  public static final class MediaSession2.CommandGroup {
-    ctor public MediaSession2.CommandGroup(android.content.Context);
-    ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup);
-    method public void addAllPredefinedCommands();
-    method public void addCommand(android.media.MediaSession2.Command);
-    method public java.util.List<android.media.MediaSession2.Command> getCommands();
-    method public boolean hasCommand(android.media.MediaSession2.Command);
-    method public boolean hasCommand(int);
-    method public void removeCommand(android.media.MediaSession2.Command);
-  }
-
-  public static final class MediaSession2.ControllerInfo {
-    method public java.lang.String getPackageName();
-    method public int getUid();
-    method public boolean isTrusted();
-  }
-
-  public static abstract interface MediaSession2.OnDataSourceMissingHelper {
-    method public abstract android.media.DataSourceDesc onDataSourceMissing(android.media.MediaSession2, android.media.MediaItem2);
-  }
-
-  public static abstract class MediaSession2.SessionCallback {
-    ctor public MediaSession2.SessionCallback(android.content.Context);
-    method public void onBufferingStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2, int);
-    method public boolean onCommandRequest(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command);
-    method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
-    method public void onCurrentMediaItemChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
-    method public void onCustomCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver);
-    method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
-    method public void onMediaPrepared(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2);
-    method public void onPlayFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onPlayFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onPlayFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
-    method public void onPlayerStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, int);
-    method public void onPlaylistChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, java.util.List<android.media.MediaItem2>, android.media.MediaMetadata2);
-    method public void onPlaylistMetadataChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2);
-    method public void onPrepareFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onPrepareFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle);
-    method public void onPrepareFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle);
-    method public void onRepeatModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
-    method public void onSetRating(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2);
-    method public void onShuffleModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int);
-  }
-
-  public abstract class MediaSessionService2 extends android.app.Service {
-    ctor public MediaSessionService2();
-    method public final android.media.MediaSession2 getSession();
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public abstract android.media.MediaSession2 onCreateSession(java.lang.String);
-    method public android.media.MediaSessionService2.MediaNotification onUpdateNotification();
-    field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2";
-    field public static final java.lang.String SERVICE_META_DATA = "android.media.session";
-  }
-
-  public static class MediaSessionService2.MediaNotification {
-    ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification);
-    method public android.app.Notification getNotification();
-    method public int getNotificationId();
-  }
-
   public final class MediaSync {
     ctor public MediaSync();
     method public android.view.Surface createInputSurface();
@@ -25358,22 +24646,6 @@
     field public static final int URI_COLUMN_INDEX = 2; // 0x2
   }
 
-  public final class SessionToken2 {
-    ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String);
-    method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle);
-    method public java.lang.String getId();
-    method public java.lang.String getPackageName();
-    method public int getType();
-    method public int getUid();
-    method public android.os.Bundle toBundle();
-    field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
-    field public static final int TYPE_SESSION = 0; // 0x0
-    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
-  }
-
-  public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation {
-  }
-
   public class SoundPool {
     ctor public deprecated SoundPool(int, int, int);
     method public final void autoPause();
@@ -25577,19 +24849,6 @@
     field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
   }
 
-  public abstract class VolumeProvider2 {
-    ctor public VolumeProvider2(android.content.Context, int, int, int);
-    method public final int getControlType();
-    method public final int getCurrentVolume();
-    method public final int getMaxVolume();
-    method public void onAdjustVolume(int);
-    method public void onSetVolumeTo(int);
-    method public final void setCurrentVolume(int);
-    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
-    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
-    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
-  }
-
   public final class VolumeShaper implements java.lang.AutoCloseable {
     method public void apply(android.media.VolumeShaper.Operation);
     method public void close();
@@ -26532,23 +25791,14 @@
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
-    method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
-    method public java.util.List<android.media.SessionToken2> getActiveSessionTokens();
     method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
-    method public java.util.List<android.media.SessionToken2> getAllSessionTokens();
-    method public java.util.List<android.media.SessionToken2> getSessionServiceTokens();
     method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
-    method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener);
   }
 
   public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
     method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
   }
 
-  public static abstract interface MediaSessionManager.OnSessionTokensChangedListener {
-    method public abstract void onSessionTokensChanged(java.util.List<android.media.SessionToken2>);
-  }
-
   public final class PlaybackState implements android.os.Parcelable {
     method public int describeContents();
     method public long getActions();
@@ -29473,7 +28723,7 @@
     field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
     field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
-    field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    field public static final java.lang.String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -33178,6 +32428,8 @@
     ctor public Handler(android.os.Handler.Callback);
     ctor public Handler(android.os.Looper);
     ctor public Handler(android.os.Looper, android.os.Handler.Callback);
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
     method public void dispatchMessage(android.os.Message);
     method public final void dump(android.util.Printer, java.lang.String);
     method public final android.os.Looper getLooper();
@@ -40044,6 +39296,7 @@
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
     field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
@@ -41408,7 +40661,7 @@
     method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
     field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
     field public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2; // 0x2
-    field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4; // 0x4
+    field public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4; // 0x4
     field public static final int HANDOVER_FAILURE_UNKNOWN = 5; // 0x5
     field public static final int HANDOVER_FAILURE_USER_REJECTED = 3; // 0x3
   }
@@ -43340,21 +42593,21 @@
   public class ApnSetting implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String getApnName();
+    method public int getApnTypeBitmask();
     method public int getAuthType();
     method public java.lang.String getEntryName();
     method public int getId();
-    method public int getMmsPort();
-    method public java.net.InetAddress getMmsProxy();
-    method public java.net.URL getMmsc();
-    method public java.lang.String getMvnoType();
+    method public java.net.InetAddress getMmsProxyAddress();
+    method public int getMmsProxyPort();
+    method public android.net.Uri getMmsc();
+    method public int getMvnoType();
     method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
-    method public int getPort();
-    method public java.lang.String getProtocol();
-    method public java.net.InetAddress getProxy();
-    method public java.lang.String getRoamingProtocol();
-    method public java.util.List<java.lang.String> getTypes();
+    method public int getProtocol();
+    method public java.net.InetAddress getProxyAddress();
+    method public int getProxyPort();
+    method public int getRoamingProtocol();
     method public java.lang.String getUser();
     method public boolean isEnabled();
     method public void writeToParcel(android.os.Parcel, int);
@@ -43363,46 +42616,45 @@
     field public static final int AUTH_TYPE_PAP = 1; // 0x1
     field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
     field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
-    field public static final java.lang.String MVNO_TYPE_GID = "gid";
-    field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
-    field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
-    field public static final java.lang.String MVNO_TYPE_SPN = "spn";
-    field public static final java.lang.String PROTOCOL_IP = "IP";
-    field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
-    field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
-    field public static final java.lang.String PROTOCOL_PPP = "PPP";
-    field public static final java.lang.String TYPE_ALL = "*";
-    field public static final java.lang.String TYPE_CBS = "cbs";
-    field public static final java.lang.String TYPE_DEFAULT = "default";
-    field public static final java.lang.String TYPE_DUN = "dun";
-    field public static final java.lang.String TYPE_EMERGENCY = "emergency";
-    field public static final java.lang.String TYPE_FOTA = "fota";
-    field public static final java.lang.String TYPE_HIPRI = "hipri";
-    field public static final java.lang.String TYPE_IA = "ia";
-    field public static final java.lang.String TYPE_IMS = "ims";
-    field public static final java.lang.String TYPE_MMS = "mms";
-    field public static final java.lang.String TYPE_SUPL = "supl";
+    field public static final int MVNO_TYPE_GID = 2; // 0x2
+    field public static final int MVNO_TYPE_ICCID = 3; // 0x3
+    field public static final int MVNO_TYPE_IMSI = 1; // 0x1
+    field public static final int MVNO_TYPE_SPN = 0; // 0x0
+    field public static final int PROTOCOL_IP = 0; // 0x0
+    field public static final int PROTOCOL_IPV4V6 = 2; // 0x2
+    field public static final int PROTOCOL_IPV6 = 1; // 0x1
+    field public static final int PROTOCOL_PPP = 3; // 0x3
+    field public static final int TYPE_CBS = 128; // 0x80
+    field public static final int TYPE_DEFAULT = 17; // 0x11
+    field public static final int TYPE_DUN = 8; // 0x8
+    field public static final int TYPE_EMERGENCY = 512; // 0x200
+    field public static final int TYPE_FOTA = 32; // 0x20
+    field public static final int TYPE_HIPRI = 16; // 0x10
+    field public static final int TYPE_IA = 256; // 0x100
+    field public static final int TYPE_IMS = 64; // 0x40
+    field public static final int TYPE_MMS = 2; // 0x2
+    field public static final int TYPE_SUPL = 4; // 0x4
   }
 
   public static class ApnSetting.Builder {
     ctor public ApnSetting.Builder();
     method public android.telephony.data.ApnSetting build();
     method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
     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 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 setMmsProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri);
+    method public android.telephony.data.ApnSetting.Builder setMvnoType(int);
     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);
-    method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
-    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+    method public android.telephony.data.ApnSetting.Builder setProtocol(int);
+    method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int);
     method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
   }
 
@@ -51581,7 +50833,6 @@
     field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
     field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
     field public static final int RECORD_UNTIL_FULL = 0; // 0x0
-    field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2
   }
 
   public static class TracingConfig.Builder {
@@ -51988,6 +51239,7 @@
     method public android.webkit.WebChromeClient getWebChromeClient();
     method public static java.lang.ClassLoader getWebViewClassLoader();
     method public android.webkit.WebViewClient getWebViewClient();
+    method public android.os.Looper getWebViewLooper();
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
@@ -53423,20 +52675,6 @@
     method public void update();
   }
 
-  public class MediaControlView2 extends android.view.ViewGroup {
-    ctor public MediaControlView2(android.content.Context);
-    ctor public MediaControlView2(android.content.Context, android.util.AttributeSet);
-    ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int);
-    ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int, int);
-    method public void requestPlayButtonFocus();
-    method public void setMediaSessionToken(android.media.SessionToken2);
-    method public void setOnFullScreenListener(android.widget.MediaControlView2.OnFullScreenListener);
-  }
-
-  public static abstract interface MediaControlView2.OnFullScreenListener {
-    method public abstract void onFullScreen(android.view.View, boolean);
-  }
-
   public class MediaController extends android.widget.FrameLayout {
     ctor public MediaController(android.content.Context, android.util.AttributeSet);
     ctor public MediaController(android.content.Context, boolean);
@@ -54827,27 +54065,6 @@
     method public void suspend();
   }
 
-  public class VideoView2 extends android.view.ViewGroup {
-    ctor public VideoView2(android.content.Context);
-    ctor public VideoView2(android.content.Context, android.util.AttributeSet);
-    ctor public VideoView2(android.content.Context, android.util.AttributeSet, int);
-    ctor public VideoView2(android.content.Context, android.util.AttributeSet, int, int);
-    method public android.widget.MediaControlView2 getMediaControlView2();
-    method public android.media.SessionToken2 getMediaSessionToken();
-    method public int getViewType();
-    method public boolean isSubtitleEnabled();
-    method public void setAudioAttributes(android.media.AudioAttributes);
-    method public void setAudioFocusRequest(int);
-    method public void setDataSource(android.media.DataSourceDesc);
-    method public void setMediaControlView2(android.widget.MediaControlView2, long);
-    method public void setMediaItem(android.media.MediaItem2);
-    method public void setSpeed(float);
-    method public void setSubtitleEnabled(boolean);
-    method public void setViewType(int);
-    field public static final int VIEW_TYPE_SURFACEVIEW = 1; // 0x1
-    field public static final int VIEW_TYPE_TEXTUREVIEW = 2; // 0x2
-  }
-
   public class ViewAnimator extends android.widget.FrameLayout {
     ctor public ViewAnimator(android.content.Context);
     ctor public ViewAnimator(android.content.Context, android.util.AttributeSet);
diff --git a/api/removed.txt b/api/removed.txt
index a5370f4..1228fd1 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -155,6 +155,13 @@
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public deprecated boolean getAsAlphaMask();
     method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+    field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
+    field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
+    field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
+  }
+
+  public static deprecated class ImageDecoder.IncompleteException extends java.io.IOException {
+    ctor public ImageDecoder.IncompleteException();
   }
 
   public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
diff --git a/api/system-current.txt b/api/system-current.txt
index 1080669..d602b56 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32,6 +32,7 @@
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+    field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
     field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
@@ -99,6 +100,8 @@
     field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+    field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
@@ -179,6 +182,7 @@
     field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
+    field public static final java.lang.String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -205,7 +209,7 @@
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
-    field public static final int userRestriction = 16844165; // 0x1010585
+    field public static final int userRestriction = 16844164; // 0x1010584
   }
 
   public static final class R.raw {
@@ -276,11 +280,12 @@
     field public static final java.lang.String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume";
     field public static final java.lang.String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
     field public static final java.lang.String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
-    field public static final java.lang.String OPSTR_BIND_ACCESSIBILITY_SERVICE = "bind_accessibility_service";
-    field public static final java.lang.String OPSTR_CHANGE_WIFI_STATE = "change_wifi_state";
+    field public static final java.lang.String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
+    field public static final java.lang.String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     field public static final java.lang.String OPSTR_GET_ACCOUNTS = "android:get_accounts";
     field public static final java.lang.String OPSTR_GPS = "android:gps";
     field public static final java.lang.String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+    field public static final java.lang.String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
     field public static final java.lang.String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
     field public static final java.lang.String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
     field public static final java.lang.String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -289,7 +294,7 @@
     field public static final java.lang.String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final java.lang.String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
     field public static final java.lang.String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
-    field public static final java.lang.String OPSTR_REQUEST_DELETE_PACKAGES = "request_delete_packages";
+    field public static final java.lang.String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final java.lang.String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
     field public static final java.lang.String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     field public static final java.lang.String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
@@ -373,7 +378,6 @@
   }
 
   public final class StatsManager {
-    method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
     method public boolean addConfiguration(long, byte[]);
     method public byte[] getData(long);
     method public byte[] getMetadata();
@@ -724,6 +728,9 @@
     method public java.lang.String getNotificationChannelId();
     field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
     field public static final int NOTIFICATION_SEEN = 10; // 0xa
+    field public static final int SLICE_PINNED = 14; // 0xe
+    field public static final int SLICE_PINNED_PRIV = 13; // 0xd
+    field public static final int SYSTEM_INTERACTION = 6; // 0x6
   }
 
   public final class UsageStatsManager {
@@ -1012,15 +1019,19 @@
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public android.os.PersistableBundle getSuspendedPackageAppExtras(java.lang.String);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public boolean isPackageSuspended(java.lang.String);
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
     method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
+    method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+    method public void setSuspendedPackageAppExtras(java.lang.String, android.os.PersistableBundle);
     method public abstract void setUpdateAvailable(java.lang.String, boolean);
     method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -1233,7 +1244,9 @@
 
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+    method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+    method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
   }
@@ -2220,6 +2233,21 @@
 
 }
 
+package android.hardware.soundtrigger {
+
+  public class SoundTrigger {
+    field public static final int STATUS_OK = 0; // 0x0
+  }
+
+  public static class SoundTrigger.RecognitionEvent {
+    method public android.media.AudioFormat getCaptureFormat();
+    method public int getCaptureSession();
+    method public byte[] getData();
+    method public boolean isCaptureAvailable();
+  }
+
+}
+
 package android.hardware.usb {
 
   public class UsbDeviceConnection {
@@ -2735,6 +2763,16 @@
 
 package android.media.soundtrigger {
 
+  public abstract class SoundTriggerDetectionService extends android.app.Service {
+    ctor public SoundTriggerDetectionService();
+    method public void onConnected(java.util.UUID, android.os.Bundle);
+    method public void onDisconnected(java.util.UUID, android.os.Bundle);
+    method public void onError(java.util.UUID, android.os.Bundle, int, int);
+    method public void onGenericRecognitionEvent(java.util.UUID, android.os.Bundle, int, android.hardware.soundtrigger.SoundTrigger.RecognitionEvent);
+    method public abstract void onStopOperation(java.util.UUID, android.os.Bundle, int);
+    method public final void operationFinished(java.util.UUID, int);
+  }
+
   public final class SoundTriggerDetector {
     method public boolean startRecognition(int);
     method public boolean stopRecognition();
@@ -2759,6 +2797,7 @@
   public final class SoundTriggerManager {
     method public android.media.soundtrigger.SoundTriggerDetector createSoundTriggerDetector(java.util.UUID, android.media.soundtrigger.SoundTriggerDetector.Callback, android.os.Handler);
     method public void deleteModel(java.util.UUID);
+    method public int getDetectionServiceOperationsTimeout();
     method public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
     method public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
@@ -3035,8 +3074,10 @@
   }
 
   public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+    method public void addAddress(android.net.LinkAddress) throws java.io.IOException;
     method public void close();
     method public java.lang.String getInterfaceName();
+    method public void removeAddress(android.net.LinkAddress) throws java.io.IOException;
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -3770,7 +3811,6 @@
 
   public class IncidentManager {
     method public void reportIncident(android.os.IncidentReportArgs);
-    method public void reportIncident(java.lang.String, byte[]);
   }
 
   public final class IncidentReportArgs implements android.os.Parcelable {
@@ -3781,7 +3821,6 @@
     method public boolean containsSection(int);
     method public int describeContents();
     method public boolean isAll();
-    method public static android.os.IncidentReportArgs parseSetting(java.lang.String) throws java.lang.IllegalArgumentException;
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
@@ -4204,7 +4243,7 @@
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
-    field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
+    field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
     field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
     field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
@@ -4279,7 +4318,6 @@
     method public int getUserSecretType();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainProtectionParams> CREATOR;
-    field public static final int TYPE_CUSTOM_PASSWORD = 101; // 0x65
     field public static final int TYPE_LOCKSCREEN = 100; // 0x64
     field public static final int UI_FORMAT_PASSWORD = 2; // 0x2
     field public static final int UI_FORMAT_PATTERN = 3; // 0x3
@@ -4311,11 +4349,14 @@
   }
 
   public final class KeyDerivationParams implements android.os.Parcelable {
+    method public static android.security.keystore.recovery.KeyDerivationParams createScryptParams(byte[], int);
     method public static android.security.keystore.recovery.KeyDerivationParams createSha256Params(byte[]);
     method public int describeContents();
     method public int getAlgorithm();
+    method public int getMemoryDifficulty();
     method public byte[] getSalt();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ALGORITHM_SCRYPT = 2; // 0x2
     field public static final int ALGORITHM_SHA256 = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyDerivationParams> CREATOR;
   }
@@ -4334,15 +4375,14 @@
     method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
     method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
     method public android.security.keystore.recovery.KeyChainSnapshot getKeyChainSnapshot() throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates();
     method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
     method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
@@ -4356,9 +4396,11 @@
 
   public class RecoverySession implements java.lang.AutoCloseable {
     method public void close();
-    method public java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
+    method public java.util.Map<java.lang.String, java.security.Key> recoverKeyChainSnapshot(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
+    method public deprecated java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
     method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public byte[] start(java.lang.String, java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
   }
 
   public class SessionExpiredException extends java.security.GeneralSecurityException {
@@ -5445,6 +5487,7 @@
 
   public class EuiccManager {
     method public void continueOperation(android.content.Intent, android.os.Bundle);
+    method public void eraseSubscriptions(android.app.PendingIntent);
     method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method public int getOtaStatus();
diff --git a/api/test-current.txt b/api/test-current.txt
index 3ddbdba..dd16771 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -74,11 +74,12 @@
     field public static final java.lang.String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume";
     field public static final java.lang.String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
     field public static final java.lang.String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
-    field public static final java.lang.String OPSTR_BIND_ACCESSIBILITY_SERVICE = "bind_accessibility_service";
-    field public static final java.lang.String OPSTR_CHANGE_WIFI_STATE = "change_wifi_state";
+    field public static final java.lang.String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
+    field public static final java.lang.String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     field public static final java.lang.String OPSTR_GET_ACCOUNTS = "android:get_accounts";
     field public static final java.lang.String OPSTR_GPS = "android:gps";
     field public static final java.lang.String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+    field public static final java.lang.String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
     field public static final java.lang.String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
     field public static final java.lang.String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
     field public static final java.lang.String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -87,7 +88,7 @@
     field public static final java.lang.String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final java.lang.String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
     field public static final java.lang.String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
-    field public static final java.lang.String OPSTR_REQUEST_DELETE_PACKAGES = "request_delete_packages";
+    field public static final java.lang.String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final java.lang.String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
     field public static final java.lang.String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     field public static final java.lang.String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
@@ -294,6 +295,14 @@
 
 }
 
+package android.graphics {
+
+  public final class ImageDecoder implements java.lang.AutoCloseable {
+    method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
+  }
+
+}
+
 package android.graphics.drawable {
 
   public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -370,7 +379,9 @@
 
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+    method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+    method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
   }
@@ -405,20 +416,20 @@
     ctor public GnssMeasurement();
     method public void reset();
     method public void resetAutomaticGainControlLevel();
-    method public void resetCarrierCycles();
+    method public deprecated void resetCarrierCycles();
     method public void resetCarrierFrequencyHz();
-    method public void resetCarrierPhase();
-    method public void resetCarrierPhaseUncertainty();
+    method public deprecated void resetCarrierPhase();
+    method public deprecated void resetCarrierPhaseUncertainty();
     method public void resetSnrInDb();
     method public void set(android.location.GnssMeasurement);
     method public void setAccumulatedDeltaRangeMeters(double);
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
     method public void setAutomaticGainControlLevelInDb(double);
-    method public void setCarrierCycles(long);
+    method public deprecated void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
-    method public void setCarrierPhase(double);
-    method public void setCarrierPhaseUncertainty(double);
+    method public deprecated void setCarrierPhase(double);
+    method public deprecated void setCarrierPhaseUncertainty(double);
     method public void setCn0DbHz(double);
     method public void setConstellationType(int);
     method public void setMultipathIndicator(int);
@@ -430,6 +441,7 @@
     method public void setState(int);
     method public void setSvid(int);
     method public void setTimeOffsetNanos(double);
+    field public static final int ADR_STATE_ALL = 31; // 0x1f
   }
 
   public final class GnssMeasurementsEvent implements android.os.Parcelable {
@@ -473,7 +485,6 @@
 
   public class IncidentManager {
     method public void reportIncident(android.os.IncidentReportArgs);
-    method public void reportIncident(java.lang.String, byte[]);
   }
 
   public final class IncidentReportArgs implements android.os.Parcelable {
@@ -484,7 +495,6 @@
     method public boolean containsSection(int);
     method public int describeContents();
     method public boolean isAll();
-    method public static android.os.IncidentReportArgs parseSetting(java.lang.String) throws java.lang.IllegalArgumentException;
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
@@ -570,7 +580,7 @@
   }
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
-    field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
+    field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
     field public static final java.lang.String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
     field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
     field public static final java.lang.String LOW_POWER_MODE = "low_power";
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index ab4e764..5065a56 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -731,25 +731,25 @@
             android_logger_list_free);
 
     if (android_logger_open(loggers.get(), mLogID) == NULL) {
-        ALOGW("LogSection %s: Can't get logger.", this->name.string());
-        return NO_ERROR;
+        ALOGE("LogSection %s: Can't get logger.", this->name.string());
+        return -1;
     }
 
     log_msg msg;
     log_time lastTimestamp(0);
 
-    status_t err = NO_ERROR;
     ProtoOutputStream proto;
     while (true) {  // keeps reading until logd buffer is fully read.
-        err = android_logger_list_read(loggers.get(), &msg);
+        status_t err = android_logger_list_read(loggers.get(), &msg);
         // 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
         // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data.
         if (err <= 0) {
             if (err != -EAGAIN) {
-                ALOGE("LogSection %s: fails to read a log_msg.\n", this->name.string());
+                ALOGW("LogSection %s: fails to read a log_msg.\n", this->name.string());
             }
+            // dump previous logs and don't consider this error a failure.
             break;
         }
         if (mBinary) {
@@ -818,7 +818,7 @@
             AndroidLogEntry entry;
             err = android_log_processLogBuffer(&msg.entry_v1, &entry);
             if (err != NO_ERROR) {
-                ALOGE("LogSection %s: fails to process to an entry.\n", this->name.string());
+                ALOGW("LogSection %s: fails to process to an entry.\n", this->name.string());
                 break;
             }
             lastTimestamp.tv_sec = entry.tv_sec;
@@ -840,7 +840,7 @@
     }
     gLastLogsRetrieved[mLogID] = lastTimestamp;
     proto.flush(pipeWriteFd);
-    return err;
+    return NO_ERROR;
 }
 
 // ================================================================================
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5d6a1d1a..79c0d71 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -135,6 +135,12 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
+# Enable sanitizer on eng builds
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+    LOCAL_CLANG := true
+    LOCAL_SANITIZE := address
+endif
+
 LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
@@ -177,6 +183,7 @@
     tests/LogEvent_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/StatsLogProcessor_test.cpp \
+    tests/StatsService_test.cpp \
     tests/UidMap_test.cpp \
     tests/FieldValue_test.cpp \
     tests/condition/CombinationConditionTracker_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 652ec9d..a35570b 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -112,8 +112,8 @@
 }
 
 void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
-    if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
-        android::util::kAtomsWithAttributionChain.end()) {
+    if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
+        android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
         for (auto& value : *(event->getMutableValues())) {
             if (value.mField.getPosAtDepth(0) > kAttributionField) {
                 break;
@@ -123,12 +123,20 @@
                 updateUid(&value.mValue, hostUid);
             }
         }
-    } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
-                       android::util::kAtomsWithUidField.end() &&
-               event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) {
-        Value& value = (*event->getMutableValues())[0].mValue;
-        const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
-        updateUid(&value, hostUid);
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto,
+                                        // starting from 1
+            if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+                (event->getValues())[uidField - 1].mValue.getType() == INT) {
+                Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+                const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+                updateUid(&value, hostUid);
+            } else {
+                ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+            }
+        }
     }
 }
 
@@ -231,14 +239,13 @@
     }
 }
 
+/*
+ * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs,
                                      vector<uint8_t>* outData) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-    onDumpReportLocked(key, dumpTimeStampNs, outData);
-}
 
-void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
-                                           vector<uint8_t>* outData) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -261,35 +268,14 @@
     // Start of ConfigMetricsReport (reports).
     uint64_t reportsToken =
             proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
-
-    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
-    int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
-
-    // First, fill in ConfigMetricsReport using current data on memory, which
-    // starts from filling in StatsLogReport's.
-    it->second->onDumpReport(dumpTimeStampNs, &proto);
-
-    // Fill in UidMap.
-    vector<uint8_t> uidMap;
-    mUidMap->getOutput(key, &uidMap);
-    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMap.data());
-
-    // Fill in the timestamps.
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
-                (long long)lastReportTimeNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
-                (long long)dumpTimeStampNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
-                (long long)lastReportWallClockNs);
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
-                (long long)getWallClockNs());
-
-    // End of ConfigMetricsReport (reports).
+    onConfigMetricsReportLocked(key, dumpTimeStampNs, &proto);
     proto.end(reportsToken);
+    // End of ConfigMetricsReport (reports).
+
 
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
-    StorageManager::appendConfigMetricsReport(proto);
+    StorageManager::appendConfigMetricsReport(key, &proto);
 
     if (outData != nullptr) {
         outData->clear();
@@ -307,6 +293,40 @@
     StatsdStats::getInstance().noteMetricsReportSent(key);
 }
 
+/*
+ * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData.
+ */
+void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
+                                                    const uint64_t dumpTimeStampNs,
+                                                    ProtoOutputStream* proto) {
+    // We already checked whether key exists in mMetricsManagers in
+    // WriteDataToDisk.
+    auto it = mMetricsManagers.find(key);
+    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
+    int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
+
+    // First, fill in ConfigMetricsReport using current data on memory, which
+    // starts from filling in StatsLogReport's.
+    it->second->onDumpReport(dumpTimeStampNs, proto);
+
+    // Fill in UidMap.
+    uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
+    mUidMap->appendUidMap(key, proto);
+    proto->end(uidMapToken);
+
+    // Fill in the timestamps.
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
+                (long long)lastReportTimeNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
+                (long long)dumpTimeStampNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
+                (long long)lastReportWallClockNs);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
+                (long long)getWallClockNs());
+
+
+}
+
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
@@ -340,7 +360,7 @@
         metricsManager.dropData(timestampNs);
         StatsdStats::getInstance().noteDataDropped(key);
         VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
-    } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
+    } else if (totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) {
         // Send broadcast so that receivers can pull data.
         auto lastBroadcastTime = mLastBroadcastTimes.find(key);
         if (lastBroadcastTime != mLastBroadcastTimes.end()) {
@@ -360,11 +380,17 @@
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (auto& pair : mMetricsManagers) {
         const ConfigKey& key = pair.first;
-        vector<uint8_t> data;
-        onDumpReportLocked(key, getElapsedRealtimeNs(), &data);
+        ProtoOutputStream proto;
+        onConfigMetricsReportLocked(key, getElapsedRealtimeNs(), &proto);
         string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
              (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
-        StorageManager::writeFile(file_name.c_str(), &data[0], data.size());
+        android::base::unique_fd fd(open(file_name.c_str(),
+                                    O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
+        if (fd == -1) {
+            VLOG("Attempt to write %s but failed", file_name.c_str());
+            return;
+        }
+        proto.flush(fd.get());
     }
 }
 
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8b42146..1be4dc5 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -86,8 +86,8 @@
 
     sp<AlarmMonitor> mPeriodicAlarmMonitor;
 
-    void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs,
-                            vector<uint8_t>* outData);
+    void onConfigMetricsReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
+                                     util::ProtoOutputStream* proto);
 
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
      * actually delete the data. */
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c5dfc44..b86646a 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -867,14 +867,11 @@
                                       bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), key);
-        StatsdConfig cfg;
-        if (config.empty() || !cfg.ParseFromArray(&config[0], config.size())) {
+        if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+            *success = true;
+        } else {
             *success = false;
-            return Status::ok();
         }
-        mConfigManager->UpdateConfig(configKey, cfg);
-        *success = true;
         return Status::ok();
     } else {
         *success = false;
@@ -882,6 +879,18 @@
     }
 }
 
+bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
+    ConfigKey configKey(uid, key);
+    StatsdConfig cfg;
+    if (config.size() > 0) {  // If the config is empty, skip parsing.
+        if (!cfg.ParseFromArray(&config[0], config.size())) {
+            return false;
+        }
+    }
+    mConfigManager->UpdateConfig(configKey, cfg);
+    return true;
+}
+
 Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 02b6124..0ec31ef 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,6 +17,7 @@
 #ifndef STATS_SERVICE_H
 #define STATS_SERVICE_H
 
+#include <gtest/gtest_prod.h>
 #include "StatsLogProcessor.h"
 #include "anomaly/AlarmMonitor.h"
 #include "config/ConfigManager.h"
@@ -216,6 +217,11 @@
     status_t cmd_clear_puller_cache(FILE* out);
 
     /**
+     * Adds a configuration after checking permissions and obtaining UID from binder call.
+     */
+    bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
+
+    /**
      * Update a configuration.
      */
     void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -254,6 +260,10 @@
      * Whether this is an eng build.
      */
     bool mEngBuild;
+
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
+    FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index d85157c..79067eb 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -68,10 +68,10 @@
     if (itr->second != nullptr && timestampNs >= NS_PER_SEC * itr->second->timestampSec) {
         declareAnomaly(timestampNs, dimensionKey);
     }
-    mAlarms.erase(dimensionKey);
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->remove(itr->second);
     }
+    mAlarms.erase(dimensionKey);
 }
 
 void DurationAnomalyTracker::cancelAllAlarms() {
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 19d00b7..a2a03b1 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -67,4 +67,7 @@
 extend google.protobuf.FieldOptions {
     // Flags to decorate an atom that presents a state change.
     optional StateAtomFieldOption stateFieldOption = 50000;
+
+    // Flags to decorate the uid fields in an atom.
+    optional bool is_uid = 50001 [default = false];
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 31ca13a..99611f4 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -23,6 +23,7 @@
 
 import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
 import "frameworks/base/core/proto/android/app/enums.proto";
+import "frameworks/base/core/proto/android/app/job/enums.proto";
 import "frameworks/base/core/proto/android/bluetooth/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
@@ -115,6 +116,8 @@
         HardwareFailed hardware_failed = 72;
         PhysicalDropDetected physical_drop_detected = 73;
         ChargeCyclesReported charge_cycles_reported = 74;
+        MobileConnectionStateChanged mobile_connection_state_changed = 75;
+        MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
     }
 
     // Pulled events will start at field 10000.
@@ -207,7 +210,7 @@
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message UidProcessStateChanged {
-    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY];
+    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY, (is_uid) = true];
 
     // The state, from frameworks/base/core/proto/android/app/enums.proto.
     optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE];
@@ -221,7 +224,7 @@
  */
 message ProcessLifeCycleStateChanged {
     // TODO: should be a string tagged w/ uid annotation
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name (usually same as the app name).
     optional string name = 2;
@@ -360,7 +363,10 @@
     }
     optional State state = 3;
 
-    // TODO: Consider adding the stopReason (int)
+    // The reason a job has stopped.
+    // This is only applicable when the state is FINISHED.
+    // The default value is CANCELED.
+    optional android.app.job.StopReasonEnum stop_reason = 4;
 }
 
 /**
@@ -591,7 +597,7 @@
  */
 message MobileRadioPowerStateChanged {
     // TODO: Add attribution instead of uid?
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -606,7 +612,7 @@
  */
 message WifiRadioPowerStateChanged {
     // TODO: Add attribution instead of uid?
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -911,6 +917,56 @@
     optional int32 uiMode = 17;
 }
 
+
+/**
+ * Logs changes in the connection state of the mobile radio.
+ *
+ * Logged from:
+ *    frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+ */
+message MobileConnectionStateChanged {
+    // States are from the state machine DataConnection.java.
+    enum State {
+        UNKNOWN = 0;
+        // The connection is inactive, or disconnected.
+        INACTIVE = 1;
+        // The connection is being activated, or connecting.
+        ACTIVATING = 2;
+        // The connection is active, or connected.
+        ACTIVE = 3;
+        // The connection is disconnecting.
+        DISCONNECTING = 4;
+        // The connection is disconnecting after creating a connection.
+        DISCONNECTION_ERROR_CREATING_CONNECTION = 5;
+    }
+    optional State state  = 1;
+    // For multi-sim phones, this distinguishes between the sim cards.
+    optional int32 sim_slot_index = 2;
+    // Used to identify the connection. Starts at 0 and increments by 1 for
+    // every new network created. Resets whenever the device reboots.
+    optional int32 data_connection_id = 3;
+    // A bitmask for the capabilities of this connection.
+    // Eg. DEFAULT (internet), MMS, SUPL, DUN, IMS.
+    // Default value (if we have no information): 0
+    optional int64 capabilities = 4;
+    // If this connection has internet.
+    // This just checks if the DEFAULT bit of capabilities is set.
+    optional bool has_internet = 5;
+}
+
+/**
+ * Logs changes in mobile radio technology. eg: LTE, EDGE, CDMA.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java
+ */
+message MobileRadioTechnologyChanged {
+    optional android.telephony.NetworkTypeEnum state = 1;
+    // For multi-sim phones, this distinguishes between the sim cards.
+    optional int32 sim_slot_index = 2;
+}
+
+
 /**
  * Logs when Bluetooth is enabled and disabled.
  *
@@ -1078,7 +1134,8 @@
  */
 message DaveyOccurred {
     // The UID that logged this atom.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
+    ;
 
     // Amount of time it took to render the frame. Should be >=700ms.
     optional int64 jank_duration_millis = 2;
@@ -1098,7 +1155,7 @@
 /**
  * Logs that a setting was updated.
  * Logged from:
- *   frameworks/base/core/java/android/provider/Settings.java
+ *   frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
  * The tag and is_default allow resetting of settings to default values based on the specified
  * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
  */
@@ -1124,8 +1181,14 @@
     // True if this setting with tag should be resettable.
     optional bool is_default = 6;
 
-    // The user ID associated. Defined in android/os/UserHandle.java
+    // The associated user (for multi-user feature). Defined in android/os/UserHandle.java
     optional int32 user = 7;
+
+    enum ChangeReason {
+        UPDATED = 1; // Updated can be an insertion or an update.
+        DELETED = 2;
+    }
+    optional ChangeReason reason = 8;
 }
 
 /**
@@ -1135,7 +1198,7 @@
   *   frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
  */
 message ActivityForegroundStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional string pkg_name = 2;
     optional string class_name = 3;
 
@@ -1153,7 +1216,7 @@
  */
 message DropboxErrorChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
     optional string tag = 2;
@@ -1184,7 +1247,7 @@
  */
 message AppBreadcrumbReported {
     // The uid of the application that sent this custom atom.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
     optional int32 label = 2;
@@ -1208,7 +1271,7 @@
  */
 message AnomalyDetected {
     // Uid that owns the config whose anomaly detection alert fired.
-    optional int32 config_uid = 1;
+    optional int32 config_uid = 1 [(is_uid) = true];
 
     // Id of the config whose anomaly detection alert fired.
     optional int64 config_id = 2;
@@ -1219,7 +1282,7 @@
 
 message AppStartChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1266,7 +1329,7 @@
 
 message AppStartCancelChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1286,7 +1349,7 @@
 
 message AppStartFullyDrawnChanged {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The app package name.
     optional string pkg_name = 2;
@@ -1317,7 +1380,7 @@
  */
 message PictureInPictureStateChanged {
     // -1 if it is not available
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional string short_name = 2;
 
@@ -1336,7 +1399,7 @@
  *     services/core/java/com/android/server/wm/Session.java
  */
 message OverlayStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional string package_name = 2;
 
@@ -1357,7 +1420,7 @@
  *     //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
  */
 message ForegroundServiceStateChanged {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     // package_name + "/" + class_name
     optional string short_name = 2;
 
@@ -1377,6 +1440,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message IsolatedUidChanged {
+    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
     optional int32 parent_uid = 1;
 
@@ -1398,7 +1462,7 @@
 message PacketWakeupOccurred {
     // The uid owning the socket into which the packet was delivered, or -1 if the packet was
     // delivered nowhere.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     // The interface name on which the packet was received.
     optional string iface = 2;
     // The ethertype value of the packet.
@@ -1426,7 +1490,7 @@
  */
 message AppStartMemoryStateCaptured {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1472,7 +1536,7 @@
  */
 message LmkKillOccurred {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1504,7 +1568,7 @@
  */
 message AppDied {
     // timestamp(elapsedRealtime) of record creation
-    optional uint64 timestamp_millis = 1;
+    optional uint64 timestamp_millis = 1 [(stateFieldOption).option = EXCLUSIVE];
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -1518,7 +1582,7 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are wifi)
  */
 message WifiBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1536,9 +1600,10 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are wifi)
  */
 message WifiBytesTransferByFgBg {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    // 1 denotes foreground and 0 denotes background. This is called Set in
+    // NetworkStats.
     optional int32 is_foreground = 2;
 
     optional int64 rx_bytes = 3;
@@ -1557,7 +1622,7 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
  */
 message MobileBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1575,9 +1640,10 @@
  *   StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
  */
 message MobileBytesTransferByFgBg {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    // 1 denotes foreground and 0 denotes background. This is called Set in
+    // NetworkStats.
     optional int32 is_foreground = 2;
 
     optional int64 rx_bytes = 3;
@@ -1596,7 +1662,7 @@
  *   StatsCompanionService
  */
 message BluetoothBytesTransfer {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     optional int64 rx_bytes = 2;
 
@@ -1656,7 +1722,7 @@
  * Note that isolated process uid time should be attributed to host uids.
  */
 message CpuTimePerUid {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint64 user_time_millis = 2;
     optional uint64 sys_time_millis = 3;
 }
@@ -1667,7 +1733,7 @@
  * For each uid, we order the time by descending frequencies.
  */
 message CpuTimePerUidFreq {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint32 freq_index = 2;
     optional uint64 time_millis = 3;
 }
@@ -1749,7 +1815,7 @@
  */
 message ProcessMemoryState {
     // The uid if available. -1 means not available.
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
     optional string process_name = 2;
@@ -1801,7 +1867,7 @@
  * The file contains a monotonically increasing count of time for a single boot.
  */
 message CpuActiveTime {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional uint64 time_millis = 2;
 }
 
@@ -1815,7 +1881,7 @@
  * The file contains a monotonically increasing count of time for a single boot.
  */
 message CpuClusterTime {
-    optional int32 uid = 1;
+    optional int32 uid = 1 [(is_uid) = true];
     optional int32 cluster_index = 2;
     optional uint64 time_millis = 3;
 }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 72a00cb..fb0be73 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -166,6 +166,11 @@
     // Round it to the nearest minutes. This is the limit of alarm manager.
     // In practice, we should limit it higher.
     long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
+    // Scheduled pulling should be at least 1 min apart.
+    // This can be lower in cts tests, in which case we round it to 1 min.
+    if (roundedIntervalMs < 60 * 1000) {
+        roundedIntervalMs = 60 * 1000;
+    }
     // There is only one alarm for all pulled events. So only set it to the smallest denom.
     if (roundedIntervalMs < mCurrentPullingInterval) {
         VLOG("Updating pulling interval %ld", intervalMs);
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 0b0c5c4..ea23623 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -112,7 +112,8 @@
         VLOG("Unknown pull atom id %d", tagId);
         return;
     }
-    if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) {
+    if (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+        android::util::AtomsInfo::kAtomsWithUidField.end()) {
         VLOG("No uid to merge for atom %d", tagId);
         return;
     }
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index c42514a..a4f64dd 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -96,9 +96,13 @@
 
     const static int kMaxLogSourceCount = 50;
 
-    // Max memory allowed for storing metrics per configuration. When this limit is approached,
-    // statsd will send a broadcast so that the client can fetch the data and clear this memory.
-    static const size_t kMaxMetricsBytesPerConfig = 128 * 1024;
+    // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
+    // drops the metrics data in memory.
+    static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+
+    // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
+    // data subscriber that it's time to call getData.
+    static const size_t kBytesPerConfigTriggerGetData = 128 * 1024;
 
     // Cap the UID map's memory usage to this. This should be fairly high since the UID information
     // is critical for understanding the metrics.
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 778eb8e..bd8b293 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -134,8 +134,8 @@
     uint64_t wrapperToken =
             mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
     const bool truncateTimestamp =
-        android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
-        android::util::kNotTruncatingTimestampAtomWhiteList.end();
+            android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
+            android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
     if (truncateTimestamp) {
         mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS,
             (long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs()));
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 55a281e..49034ac 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -195,8 +195,9 @@
                     protoOutput->end(atomsToken);
                 }
                 const bool truncateTimestamp =
-                    android::util::kNotTruncatingTimestampAtomWhiteList.find(mTagId) ==
-                    android::util::kNotTruncatingTimestampAtomWhiteList.end();
+                        android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(
+                                mTagId) ==
+                        android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
                 const int64_t wall_clock_ns = truncateTimestamp ?
                     truncateTimestampNsToFiveMinutes(getWallClockNs()) : getWallClockNs();
                 for (const auto& atom : bucket.mGaugeAtoms) {
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6209bbe..c773d4f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -64,15 +64,9 @@
                              mTrackerToConditionMap, mNoReportMetricIds);
 
     if (config.allowed_log_source_size() == 0) {
-        // TODO(b/70794411): uncomment the following line and remove the hard coded log source
-        // after all configs have the log source added.
-        // mConfigValid = false;
-        // ALOGE("Log source white list is empty! This config won't get any data.");
-
-        mAllowedUid.push_back(AID_ROOT);
-        mAllowedUid.push_back(AID_STATSD);
-        mAllowedUid.push_back(AID_SYSTEM);
-        mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
+        mConfigValid = false;
+        ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
+                      "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
     } else {
         for (const auto& source : config.allowed_log_source()) {
             auto it = UidMap::sAidToUidMapping.find(source);
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 335ec4c..c9547cf 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -205,11 +205,12 @@
     bool hasPendingEvent =
             false;  // has either a kStarted or kPaused event across bucket boundaries
     // meaning we need to carry them over to the new bucket.
-    for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
+    for (auto it = mInfos.begin(); it != mInfos.end();) {
         if (it->second.state == DurationState::kStopped) {
             // No need to keep buckets for events that were stopped before.
-            mInfos.erase(it);
+            it = mInfos.erase(it);
         } else {
+            ++it;
             hasPendingEvent = true;
         }
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 901f500..c6112fd 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -170,9 +170,10 @@
     // 1. must not have "stop". must have "dimension"
     if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
         // TODO: need to check the start atom matcher too.
-        auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field());
+        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
+                simplePredicate.dimensions().field());
         // 2. must be based on a state atom.
-        if (it != android::util::kStateAtomsFieldOptions.end()) {
+        if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
             // 3. dimension must be primary fields + state field IN ORDER
             size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
             vector<Matcher> dimensions;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index efbe96e..1cb20bc 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -331,25 +331,25 @@
     return mBytesUsed;
 }
 
-void UidMap::getOutput(const ConfigKey& key, vector<uint8_t>* outData) {
-    getOutput(getElapsedRealtimeNs(), key, outData);
+void UidMap::appendUidMap(const ConfigKey& key, ProtoOutputStream* proto) {
+    appendUidMap(getElapsedRealtimeNs(), key, proto);
 }
 
-void UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData) {
+void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+                          ProtoOutputStream* proto) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
 
-    ProtoOutputStream proto;
     for (const ChangeRecord& record : mChanges) {
         if (record.timestampNs > mLastUpdatePerConfigKey[key]) {
             uint64_t changesToken =
-                    proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES);
-            proto.write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
-            proto.write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
-                        (long long)record.timestampNs);
-            proto.write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
-            proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
-            proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version);
-            proto.end(changesToken);
+                    proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES);
+            proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
+            proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
+                         (long long)record.timestampNs);
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version);
+            proto->end(changesToken);
         }
     }
 
@@ -360,13 +360,14 @@
         if ((count == mSnapshots.size() - 1 && !atLeastOneSnapshot) ||
             record.timestampNs > mLastUpdatePerConfigKey[key]) {
             uint64_t snapshotsToken =
-                    proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
+                    proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
             atLeastOneSnapshot = true;
             count++;
-            proto.write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
-                        (long long)record.timestampNs);
-            proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data());
-            proto.end(snapshotsToken);
+            proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
+                         (long long)record.timestampNs);
+            proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data(),
+                         record.bytes.size());
+            proto->end(snapshotsToken);
         }
     }
 
@@ -377,17 +378,20 @@
     if (newMin > prevMin) {  // Delete anything possible now that the minimum has
                              // moved forward.
         int64_t cutoff_nanos = newMin;
-        for (auto it_snapshots = mSnapshots.begin(); it_snapshots != mSnapshots.end();
-             ++it_snapshots) {
+        for (auto it_snapshots = mSnapshots.begin(); it_snapshots != mSnapshots.end();) {
             if (it_snapshots->timestampNs < cutoff_nanos) {
                 mBytesUsed -= it_snapshots->bytes.size() + kBytesTimestampField;
-                mSnapshots.erase(it_snapshots);
+                it_snapshots = mSnapshots.erase(it_snapshots);
+            } else {
+                ++it_snapshots;
             }
         }
-        for (auto it_changes = mChanges.begin(); it_changes != mChanges.end(); ++it_changes) {
+        for (auto it_changes = mChanges.begin(); it_changes != mChanges.end();) {
             if (it_changes->timestampNs < cutoff_nanos) {
                 mBytesUsed -= kBytesChangeRecord;
-                mChanges.erase(it_changes);
+                it_changes = mChanges.erase(it_changes);
+            } else {
+                ++it_changes;
             }
         }
 
@@ -395,47 +399,36 @@
             // Produce another snapshot. This results in extra data being uploaded but
             // helps ensure we can re-construct the UID->app name, versionCode mapping
             // in server.
-            ProtoOutputStream proto;
-            uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                          FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+            ProtoOutputStream snapshotProto;
+            uint64_t token = snapshotProto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                                 FIELD_ID_SNAPSHOT_PACKAGE_INFO);
             for (const auto& it : mMap) {
-                proto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME,
-                            it.second.packageName);
-                proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                            (int)it.second.versionCode);
-                proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, (int)it.first);
+                snapshotProto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME,
+                                    it.second.packageName);
+                snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                                    (int)it.second.versionCode);
+                snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID,
+                                    (int)it.first);
             }
-            proto.end(token);
+            snapshotProto.end(token);
 
             // Copy ProtoOutputStream output to
-            auto iter = proto.data();
-            vector<char> outData(proto.size());
+            auto iter = snapshotProto.data();
+            vector<char> snapshotData(snapshotProto.size());
             size_t pos = 0;
             while (iter.readBuffer() != NULL) {
                 size_t toRead = iter.currentToRead();
-                std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+                std::memcpy(&(snapshotData[pos]), iter.readBuffer(), toRead);
                 pos += toRead;
                 iter.rp()->move(toRead);
             }
-            mSnapshots.emplace_back(timestamp, outData);
-            mBytesUsed += kBytesTimestampField + outData.size();
+            mSnapshots.emplace_back(timestamp, snapshotData);
+            mBytesUsed += kBytesTimestampField + snapshotData.size();
         }
     }
     StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
     StatsdStats::getInstance().setUidMapChanges(mChanges.size());
     StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size());
-    if (outData != nullptr) {
-        outData->clear();
-        outData->resize(proto.size());
-        size_t pos = 0;
-        auto iter = proto.data();
-        while (iter.readBuffer() != NULL) {
-            size_t toRead = iter.currentToRead();
-            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
-            pos += toRead;
-            iter.rp()->move(toRead);
-        }
-    }
 }
 
 void UidMap::printUidMap(FILE* out) const {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index b0181f7..9dc73f4 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -19,6 +19,7 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 #include "packages/PackageInfoListener.h"
+#include "stats_util.h"
 
 #include <binder/IResultReceiver.h>
 #include <binder/IShellCallback.h>
@@ -32,8 +33,11 @@
 #include <string>
 #include <unordered_map>
 
+using namespace android;
 using namespace std;
 
+using android::util::ProtoOutputStream;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -45,8 +49,8 @@
     AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){};
 };
 
-// When calling getOutput, we retrieve all the ChangeRecords since the last
-// timestamp we called getOutput for this configuration key.
+// When calling appendUidMap, we retrieve all the ChangeRecords since the last
+// timestamp we called appendUidMap for this configuration key.
 struct ChangeRecord {
     const bool deletion;
     const int64_t timestampNs;
@@ -70,8 +74,8 @@
 // less because of varint encoding).
 const unsigned int kBytesTimestampField = 10;
 
-// When calling getOutput, we retrieve all the snapshots since the last
-// timestamp we called getOutput for this configuration key.
+// When calling appendUidMap, we retrieve all the snapshots since the last
+// timestamp we called appendUidMap for this configuration key.
 struct SnapshotRecord {
     const int64_t timestampNs;
 
@@ -135,7 +139,7 @@
     // Gets all snapshots and changes that have occurred since the last output.
     // If every config key has received a change or snapshot record, then this
     // record is deleted.
-    void getOutput(const ConfigKey& key, vector<uint8_t>* outData);
+    void appendUidMap(const ConfigKey& key, util::ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -158,7 +162,8 @@
                    const int64_t& versionCode);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
-    void getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData);
+    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+                      util::ProtoOutputStream* proto);
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index cd41f53..643d2bf 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -160,38 +160,45 @@
     }
 }
 
-void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
     if (dir == NULL) {
         VLOG("Path %s does not exist", STATS_DATA_DIR);
         return;
     }
 
+    string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
+
     dirent* de;
     while ((de = readdir(dir.get()))) {
         char* name = de->d_name;
         if (name[0] == '.') continue;
-        VLOG("file %s", name);
 
-        int64_t result[3];
-        parseFileName(name, result);
-        if (result[0] == -1) continue;
-        int64_t timestamp = result[0];
-        int64_t uid = result[1];
-        int64_t configID = result[2];
-        string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
-        int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
-        if (fd != -1) {
-            string content;
-            if (android::base::ReadFdToString(fd, &content)) {
-                proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
-                            content.c_str());
+        size_t nameLen = strlen(name);
+        size_t suffixLen = suffix.length();
+        if (suffixLen <= nameLen &&
+            strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
+            int64_t result[3];
+            parseFileName(name, result);
+            if (result[0] == -1) continue;
+            int64_t timestamp = result[0];
+            int64_t uid = result[1];
+            int64_t configID = result[2];
+
+            string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
+            int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+            if (fd != -1) {
+                string content;
+                if (android::base::ReadFdToString(fd, &content)) {
+                    proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
+                                content.c_str(), content.size());
+                }
+                close(fd);
             }
-            close(fd);
-        }
 
-        // Remove file from disk after reading.
-        remove(file_name.c_str());
+            // Remove file from disk after reading.
+            remove(file_name.c_str());
+        }
     }
 }
 
@@ -254,8 +261,7 @@
         return false;
     }
 
-    const char* suffix =
-            StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+    string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
 
     dirent* de;
     while ((de = readdir(dir.get()))) {
@@ -264,10 +270,10 @@
             continue;
         }
         size_t nameLen = strlen(name);
-        size_t suffixLen = strlen(suffix);
+        size_t suffixLen = suffix.length();
         // There can be at most one file that matches this suffix (config key).
         if (suffixLen <= nameLen &&
-                strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+            strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
             int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
                                   O_RDONLY | O_CLOEXEC);
             if (fd != -1) {
@@ -275,9 +281,11 @@
                 if (android::base::ReadFdToString(fd, &content)) {
                     vector<uint8_t> vec(content.begin(), content.end());
                     if (vec == config) {
+                        close(fd);
                         return true;
                     }
                 }
+                close(fd);
             }
         }
     }
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4b75628..fb7b085 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
      * Appends ConfigMetricsReport found on disk to the specific proto and
      * delete it.
      */
-    static void appendConfigMetricsReport(ProtoOutputStream& proto);
+    static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto);
 
     /**
      * Call to load the saved configs from disk.
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
new file mode 100644
index 0000000..a7b4136
--- /dev/null
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -0,0 +1,67 @@
+// 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.
+
+#include "StatsService.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using android::util::ProtoOutputStream;
+
+#ifdef __ANDROID__
+
+TEST(StatsServiceTest, TestAddConfig_simple) {
+    StatsService service(nullptr);
+    StatsdConfig config;
+    config.set_id(12345);
+    string serialized = config.SerializeAsString();
+
+    EXPECT_TRUE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+TEST(StatsServiceTest, TestAddConfig_empty) {
+    StatsService service(nullptr);
+    string serialized = "";
+
+    EXPECT_TRUE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+TEST(StatsServiceTest, TestAddConfig_invalid) {
+    StatsService service(nullptr);
+    string serialized = "Invalid config!";
+
+    EXPECT_FALSE(
+            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index ee7d770..c9492eb 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -20,6 +20,7 @@
 #include "statslog.h"
 #include "statsd_test_util.h"
 
+#include <android/util/ProtoOutputStream.h>
 #include <gtest/gtest.h>
 
 #include <stdio.h>
@@ -30,6 +31,8 @@
 namespace os {
 namespace statsd {
 
+using android::util::ProtoOutputStream;
+
 #ifdef __ANDROID__
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
@@ -156,6 +159,20 @@
     EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
 }
 
+static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) {
+    vector<uint8_t> bytes;
+    bytes.resize(proto->size());
+    size_t pos = 0;
+    auto iter = proto->data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&((bytes)[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+    results->ParseFromArray(bytes.data(), bytes.size());
+}
+
 TEST(UidMapTest, TestClearingOutput) {
     UidMap m;
 
@@ -176,26 +193,26 @@
     m.updateMap(1, uids, versions, apps);
     EXPECT_EQ(1U, m.mSnapshots.size());
 
-    vector<uint8_t> bytes;
-    m.getOutput(2, config1, &bytes);
+    ProtoOutputStream proto;
+    m.appendUidMap(2, config1, &proto);
     UidMapping results;
-    results.ParseFromArray(bytes.data(), bytes.size());
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // It should be cleared now
     EXPECT_EQ(1U, m.mSnapshots.size());
-    bytes.clear();
-    m.getOutput(2, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(2, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // Now add another configuration.
     m.OnConfigUpdated(config2);
     m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
     EXPECT_EQ(1U, m.mChanges.size());
-    bytes.clear();
-    m.getOutput(6, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(6, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(1U, m.mChanges.size());
@@ -205,16 +222,16 @@
     EXPECT_EQ(2U, m.mChanges.size());
 
     // We still can't remove anything.
-    bytes.clear();
-    m.getOutput(8, config1, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(8, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(2U, m.mChanges.size());
 
-    bytes.clear();
-    m.getOutput(9, config2, &bytes);
-    results.ParseFromArray(bytes.data(), bytes.size());
+    proto.clear();
+    m.appendUidMap(9, config2, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(2, results.changes_size());
     // At this point both should be cleared.
@@ -242,11 +259,12 @@
     m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
     EXPECT_TRUE(m.mBytesUsed > snapshot_bytes);
 
+    ProtoOutputStream proto;
     vector<uint8_t> bytes;
-    m.getOutput(2, config1, &bytes);
+    m.appendUidMap(2, config1, &proto);
     size_t prevBytes = m.mBytesUsed;
 
-    m.getOutput(4, config1, &bytes);
+    m.appendUidMap(4, config1, &proto);
     EXPECT_TRUE(m.mBytesUsed < prevBytes);
 }
 
@@ -286,4 +304,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 2574ba7..a04a6f9 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -30,6 +30,7 @@
 
 StatsdConfig CreateStatsdConfig(const Position position) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
     auto attributionNodeMatcher =
         wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher();
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index a08f606..63e23ce 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -31,6 +31,7 @@
 StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
         DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
@@ -337,6 +338,7 @@
 StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
         DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
@@ -580,6 +582,7 @@
 StatsdConfig CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
         DurationMetric::AggregationType aggregationType) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index 435e199..2287c2b 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -30,6 +30,7 @@
 
 StatsdConfig CreateCountMetric_NoLink_CombinationCondition_Config() {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
     *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
@@ -229,6 +230,7 @@
 
 StatsdConfig CreateCountMetric_Link_CombinationCondition() {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     auto appCrashMatcher = CreateProcessCrashAtomMatcher();
     *config.add_atom_matcher() = appCrashMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
@@ -416,6 +418,7 @@
 StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition(
         DurationMetric::AggregationType aggregationType) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
     *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
@@ -603,6 +606,7 @@
 StatsdConfig CreateDurationMetricConfig_Link_CombinationCondition(
         DurationMetric::AggregationType aggregationType) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
     *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index 75ceafb..ab37140 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -31,6 +31,7 @@
 StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition(
         DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
@@ -306,6 +307,7 @@
 StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
         DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
@@ -525,6 +527,7 @@
 StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
         DurationMetric::AggregationType aggregationType) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
     *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 9ceffc8..2e6a0f0 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -30,6 +30,7 @@
 
 StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
     *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
 
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index c874d92..1440f29 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -29,6 +29,8 @@
 
 StatsdConfig CreateStatsdConfig() {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
 
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 9153795..bfae8bc 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -30,6 +30,7 @@
 
 StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
     StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
     *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 40b266f..87fb998 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -3375,8 +3375,6 @@
 HPLandroid/location/ICountryListener$Stub;->asBinder()Landroid/os/IBinder;
 HPLandroid/location/ICountryListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryListener;
 HPLandroid/location/ICountryListener;->onCountryDetected(Landroid/location/Country;)V
-HPLandroid/location/IFusedProvider$Stub;-><init>()V
-HPLandroid/location/IFusedProvider;->onFusedLocationHardwareChange(Landroid/hardware/location/IFusedLocationHardware;)V
 HPLandroid/location/IGeocodeProvider$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 HPLandroid/location/IGeocodeProvider$Stub$Proxy;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 HPLandroid/location/IGeocodeProvider$Stub;-><init>()V
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index e2121dd..9cba926 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -20,8 +20,20 @@
 Landroid/app/ActivityManager;->PROCESS_STATE_IMPORTANT_BACKGROUND:I
 Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
 Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
+Landroid/app/ActivityManager$RecentTaskInfo;->lastActiveTime:J
+Landroid/app/ActivityManager$RecentTaskInfo;->resizeMode:I
+Landroid/app/ActivityManager$RecentTaskInfo;->supportsSplitScreenMultiWindow:Z
+Landroid/app/ActivityManager$RecentTaskInfo;->userId:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
+Landroid/app/ActivityManager;->setPersistentVrThread(I)V
+Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I
+Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap;
+Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect;
+Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I
+Landroid/app/ActivityManager$TaskSnapshot;->getScale()F
+Landroid/app/ActivityManager$TaskSnapshot;->isRealSnapshot()Z
+Landroid/app/ActivityManager$TaskSnapshot;->isReducedResolution()Z
 Landroid/app/Activity;->mApplication:Landroid/app/Application;
 Landroid/app/Activity;->mComponent:Landroid/content/ComponentName;
 Landroid/app/Activity;->mFragments:Landroid/app/FragmentController;
@@ -35,6 +47,7 @@
 Landroid/app/Activity;->mToken:Landroid/os/IBinder;
 Landroid/app/Activity;->mWindow:Landroid/view/Window;
 Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/ActivityOptions;->makeMultiThumbFutureAspectScaleAnimation(Landroid/content/Context;Landroid/os/Handler;Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/app/ActivityOptions$OnAnimationStartedListener;Z)Landroid/app/ActivityOptions;
 Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
 Landroid/app/Activity;->setPersistent(Z)V
 Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
@@ -317,6 +330,13 @@
 Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
 Landroid/app/usage/UsageStats;->mLastEvent:I
 Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
+Landroid/app/Vr2dDisplayProperties$Builder;->build()Landroid/app/Vr2dDisplayProperties;
+Landroid/app/Vr2dDisplayProperties$Builder;-><init>()V
+Landroid/app/Vr2dDisplayProperties$Builder;->setEnabled(Z)Landroid/app/Vr2dDisplayProperties$Builder;
+Landroid/app/Vr2dDisplayProperties;-><init>(III)V
+Landroid/app/VrManager;->getPersistentVrModeEnabled()Z
+Landroid/app/VrManager;->registerVrStateCallback(Landroid/app/VrStateCallback;Landroid/os/Handler;)V
+Landroid/app/VrManager;->setVr2dDisplayProperties(Landroid/app/Vr2dDisplayProperties;)V
 Landroid/app/WallpaperColors;->getColorHints()I
 Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
 Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
@@ -497,6 +517,7 @@
 Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
 Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
 Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
+Landroid/content/res/ColorStateList;->getColors()[I
 Landroid/content/res/ColorStateList;->mColors:[I
 Landroid/content/res/ColorStateList;->mDefaultColor:I
 Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateList$ColorStateListFactory;
@@ -563,6 +584,7 @@
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
 Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
 Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
+Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
 Landroid/graphics/Bitmap$Config;->nativeInt:I
 Landroid/graphics/Bitmap$Config;->nativeToConfig(I)Landroid/graphics/Bitmap$Config;
@@ -643,10 +665,10 @@
 Landroid/graphics/fonts/FontVariationAxis;->mStyleValue:F
 Landroid/graphics/fonts/FontVariationAxis;->mTag:I
 Landroid/graphics/GraphicBuffer;->createFromExisting(IIIIJ)Landroid/graphics/GraphicBuffer;
+Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
 Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
 Landroid/graphics/GraphicBuffer;->mNativeObject:J
 Landroid/graphics/ImageDecoder;-><init>(JIIZ)V
-Landroid/graphics/ImageDecoder;->onPartialImage(I)Z
 Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
 Landroid/graphics/LinearGradient;->mColors:[I
 Landroid/graphics/Matrix;->native_instance:J
@@ -671,8 +693,13 @@
 Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
 Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
 Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
+Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
 Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
+Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
 Landroid/hardware/Camera;->addCallbackBuffer([BI)V
 Landroid/hardware/Camera;->mNativeContext:J
@@ -795,7 +822,6 @@
 Landroid/location/Country;->getSource()I
 Landroid/location/GeocoderParams;->getClientPackage()Ljava/lang/String;
 Landroid/location/GeocoderParams;->getLocale()Ljava/util/Locale;
-Landroid/location/IFusedProvider$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;-><init>()V
 Landroid/location/IGeofenceProvider$Stub;-><init>()V
 Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
@@ -829,6 +855,7 @@
 Landroid/media/AudioManager;->forceVolumeControlStream(I)V
 Landroid/media/AudioManager;->getOutputLatency(I)I
 Landroid/media/AudioManager;->getService()Landroid/media/IAudioService;
+Landroid/media/AudioManager;-><init>(Landroid/content/Context;)V
 Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap;
 Landroid/media/AudioManager;->setMasterMute(ZI)V
 Landroid/media/AudioManager;->startBluetoothScoVirtualCall()V
@@ -891,6 +918,7 @@
 Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
 Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
 Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
 Landroid/media/MediaCodec$CodecException;-><init>(IILjava/lang/String;)V
@@ -948,6 +976,7 @@
 Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getData()[B
 Landroid/media/soundtrigger/SoundTriggerManager;->loadSoundModel(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/app/PendingIntent;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
+Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/os/Bundle;Landroid/content/ComponentName;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
 Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
@@ -1128,6 +1157,8 @@
 Landroid/os/Binder;->execTransact(IJJI)Z
 Landroid/os/Binder;->mObject:J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/Build;->IS_DEBUGGABLE:Z
+Landroid/os/Build;->IS_EMULATOR:Z
 Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
 Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1212,8 +1243,10 @@
 Landroid/os/ParcelFileDescriptor;-><init>(Ljava/io/FileDescriptor;)V
 Landroid/os/Parcel;->mNativePtr:J
 Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
+Landroid/os/Parcel;->readParcelableList(Ljava/util/List;Ljava/lang/ClassLoader;)Ljava/util/List;
 Landroid/os/Parcel$ReadWriteHelper;-><init>()V
 Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
+Landroid/os/Parcel;->writeParcelableList(Ljava/util/List;I)V
 Landroid/os/PowerManager;->getDefaultScreenBrightnessSetting()I
 Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I
 Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I
@@ -1584,6 +1617,8 @@
 Landroid/R$styleable;->View_fitsSystemWindows:I
 Landroid/R$styleable;->View_focusable:I
 Landroid/R$styleable;->View_focusableInTouchMode:I
+Landroid/R$styleable;->ViewGroup_Layout:[I
+Landroid/R$styleable;->ViewGroup_MarginLayout:[I
 Landroid/R$styleable;->View_hapticFeedbackEnabled:I
 Landroid/R$styleable;->View:[I
 Landroid/R$styleable;->View_id:I
@@ -1673,6 +1708,7 @@
 Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
 Landroid/service/voice/AlwaysOnHotwordDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
 Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
+Landroid/service/vr/IVrManager;->getVr2dDisplayId()I
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
 Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
 Landroid/system/Int32Ref;->value:I
@@ -1814,6 +1850,7 @@
 Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
 Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
 Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
+Landroid/text/Layout;->getPrimaryHorizontal(IZ)F
 Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
 Landroid/text/SpannableStringBuilder;->mGapLength:I
 Landroid/text/SpannableStringBuilder;->mGapStart:I
@@ -1962,9 +1999,11 @@
 Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
 Landroid/view/inputmethod/InputMethodSubtypeArray;-><init>(Ljava/util/List;)V
 Landroid/view/InputQueue;->finishInputEvent(JZ)V
+Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
 Landroid/view/IWindowManager;->getAnimationScale(I)F
 Landroid/view/IWindowManager;->hasNavigationBar()Z
 Landroid/view/IWindowManager;->setAnimationScale(IF)V
+Landroid/view/IWindowManager;->setShelfHeight(ZI)V
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
 Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
@@ -2012,6 +2051,18 @@
 Landroid/view/PointerIcon;->mHotSpotX:F
 Landroid/view/PointerIcon;->mHotSpotY:F
 Landroid/view/PointerIcon;->mType:I
+Landroid/view/RemoteAnimationDefinition;->addRemoteAnimation(IILandroid/view/RemoteAnimationAdapter;)V
+Landroid/view/RemoteAnimationTarget;->clipRect:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->contentInsets:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->isNotInRecents:Z
+Landroid/view/RemoteAnimationTarget;->isTranslucent:Z
+Landroid/view/RemoteAnimationTarget;->leash:Landroid/view/SurfaceControl;
+Landroid/view/RemoteAnimationTarget;->mode:I
+Landroid/view/RemoteAnimationTarget;->position:Landroid/graphics/Point;
+Landroid/view/RemoteAnimationTarget;->prefixOrderIndex:I
+Landroid/view/RemoteAnimationTarget;->sourceContainerBounds:Landroid/graphics/Rect;
+Landroid/view/RemoteAnimationTarget;->taskId:I
+Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
 Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
 Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
 Landroid/view/ScaleGestureDetector;->mMinSpan:I
@@ -2035,6 +2086,7 @@
 Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
 Landroid/view/SurfaceView;->surfacePositionLost_uiRtSync(J)V
 Landroid/view/SurfaceView;->updateSurfacePosition_renderWorker(JIIII)V
+Landroid/view/textclassifier/Logger;->DISABLED:Landroid/view/textclassifier/Logger;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
@@ -2061,6 +2113,7 @@
 Landroid/view/View;->clearAccessibilityFocus()V
 Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z
 Landroid/view/View;->computeOpaqueFlags()V
+Landroid/view/ViewConfiguration;->getDeviceGlobalActionKeyTimeout()J
 Landroid/view/ViewConfiguration;->getDoubleTapMinTime()I
 Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
 Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
@@ -2072,11 +2125,15 @@
 Landroid/view/View;->dispatchDetachedFromWindow()V
 Landroid/view/View;->fitsSystemWindows()Z
 Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V
 Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix;
 Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
 Landroid/view/View;->getLocationOnScreen()[I
+Landroid/view/View;->getRawTextAlignment()I
+Landroid/view/View;->getRawTextDirection()I
 Landroid/view/View;->getTransitionAlpha()F
 Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
+Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V
 Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V
 Landroid/view/ViewGroup;->dispatchViewRemoved(Landroid/view/View;)V
 Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
@@ -2089,6 +2146,11 @@
 Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
 Landroid/view/ViewGroup;->mGroupFlags:I
 Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/ViewGroup;->resetResolvedDrawables()V
+Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V
+Landroid/view/ViewGroup;->resetResolvedPadding()V
+Landroid/view/ViewGroup;->resetResolvedTextAlignment()V
+Landroid/view/ViewGroup;->resetResolvedTextDirection()V
 Landroid/view/ViewGroup;->suppressLayout(Z)V
 Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
 Landroid/view/View;->internalSetPadding(IIII)V
@@ -2128,10 +2190,17 @@
 Landroid/view/View;->requestAccessibilityFocus()Z
 Landroid/view/View;->resetDisplayList()V
 Landroid/view/View;->resetPaddingToInitialValues()V
+Landroid/view/View;->resetResolvedDrawables()V
+Landroid/view/View;->resetResolvedLayoutDirection()V
+Landroid/view/View;->resetResolvedPadding()V
+Landroid/view/View;->resetResolvedTextAlignment()V
+Landroid/view/View;->resetResolvedTextDirection()V
+Landroid/view/View;->resetRtlProperties()V
 Landroid/view/ViewRootImpl;->detachFunctor(J)V
 Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V
 Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
 Landroid/view/ViewRootImpl;->mStopped:Z
+Landroid/view/ViewRootImpl;->mSurface:Landroid/view/Surface;
 Landroid/view/ViewRootImpl;->mView:Landroid/view/View;
 Landroid/view/View$ScrollabilityCache;->scrollBar:Landroid/widget/ScrollBarDrawable;
 Landroid/view/View;->setAlphaNoInvalidation(F)Z
@@ -2241,6 +2310,10 @@
 Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
 Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
 Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver;
+Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver;
+Landroid/widget/CursorAdapter;->mDataValid:Z
+Landroid/widget/CursorAdapter;->mRowIDColumn:I
 Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate;
 Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint;
 Landroid/widget/Editor;->invalidateTextDisplayList()V
@@ -2290,6 +2363,8 @@
 Landroid/widget/ListView;->fillDown(II)Landroid/view/View;
 Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View;
 Landroid/widget/ListView;->fillUp(II)Landroid/view/View;
+Landroid/widget/ListView;->findViewTraversal(I)Landroid/view/View;
+Landroid/widget/ListView;->findViewWithTagTraversal(Ljava/lang/Object;)Landroid/view/View;
 Landroid/widget/ListView;->mAreAllItemsSelectable:Z
 Landroid/widget/ListView;->setSelectionInt(I)V
 Landroid/widget/MediaController;->mAnchor:Landroid/view/View;
@@ -2375,6 +2450,8 @@
 Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
 Landroid/widget/TextView;->assumeLayout()V
 Landroid/widget/TextView;->createEditorIfNeeded()V
+Landroid/widget/TextView;->getHorizontallyScrolling()Z
+Landroid/widget/TextView;->getTextColor(Landroid/content/Context;Landroid/content/res/TypedArray;I)I
 Landroid/widget/TextView;->isSingleLine()Z
 Landroid/widget/TextView;->mCursorDrawableRes:I
 Landroid/widget/TextView;->mCurTextColor:I
@@ -2388,6 +2465,10 @@
 Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V
 Landroid/widget/Toast;->getService()Landroid/app/INotificationManager;
 Landroid/widget/Toast;->sService:Landroid/app/INotificationManager;
+Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController;
+Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V
+Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V
+Landroid/widget/VideoView2;->setVideoPath(Ljava/lang/String;)V
 Landroid/widget/VideoView;->mCurrentBufferPercentage:I
 Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController;
 Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 952b28b..2477b57 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -1,9 +1,24 @@
+Landroid/accounts/AccountManager;-><init>(Landroid/content/Context;Landroid/accounts/IAccountManager;Landroid/os/Handler;)V
+Landroid/app/Activity;->managedQuery(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
+Landroid/app/ActivityManagerNative;->broadcastStickyIntent(Landroid/content/Intent;Ljava/lang/String;I)V
 Landroid/app/ActivityManager$RecentTaskInfo;->configuration:Landroid/content/res/Configuration;
 Landroid/app/ActivityManager$TaskDescription;->loadTaskDescriptionIcon(Ljava/lang/String;I)Landroid/graphics/Bitmap;
 Landroid/app/ActivityManager$TaskSnapshot;->getSnapshot()Landroid/graphics/GraphicBuffer;
 Landroid/app/ActivityOptions;->makeRemoteAnimation(Landroid/view/RemoteAnimationAdapter;)Landroid/app/ActivityOptions;
 Landroid/app/ActivityOptions;->setSplitScreenCreateMode(I)V
 Landroid/app/Activity;->registerRemoteAnimations(Landroid/view/RemoteAnimationDefinition;)V
+Landroid/app/ActivityView;-><init>(Landroid/content/Context;)V
+Landroid/app/ActivityView;->release()V
+Landroid/app/ActivityView;->startActivity(Landroid/app/PendingIntent;)V
+Landroid/app/ActivityView;->startActivity(Landroid/content/Intent;)V
+Landroid/app/AppOpsManager;->getPackagesForOps([I)Ljava/util/List;
+Landroid/app/AppOpsManager;->getToken(Lcom/android/internal/app/IAppOpsService;)Landroid/os/IBinder;
+Landroid/app/AppOpsManager$OpEntry;->getOp()I
+Landroid/app/AppOpsManager$OpEntry;->getTime()J
+Landroid/app/AppOpsManager$OpEntry;->isRunning()Z
+Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List;
+Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String;
+Landroid/app/AppOpsManager$PackageOps;->getUid()I
 Landroid/app/IActivityController$Stub;-><init>()V
 Landroid/app/IActivityManager;->cancelRecentsAnimation()V
 Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
@@ -11,12 +26,18 @@
 Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
 Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
 Landroid/app/IActivityManager;->getLockTaskModeState()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
 Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
 Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
 Landroid/app/IActivityManager;->removeTask(I)Z
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
 Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
 Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
+Landroid/app/IAlarmManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IAlarmManager;
 Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
 Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IAssistDataReceiver$Stub;-><init>()V
@@ -40,6 +61,8 @@
 Landroid/app/VrStateCallback;-><init>()V
 Landroid/app/VrStateCallback;->onPersistentVrStateChanged(Z)V
 Landroid/app/WallpaperColors;-><init>(Landroid/graphics/Color;Landroid/graphics/Color;Landroid/graphics/Color;I)V
+Landroid/bluetooth/BluetoothHeadset;->phoneStateChanged(IIILjava/lang/String;I)V
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
 Landroid/companion/AssociationRequest;->getDeviceFilters()Ljava/util/List;
 Landroid/companion/AssociationRequest;->isSingleDevice()Z
 Landroid/companion/BluetoothDeviceFilter;->getAddress()Ljava/lang/String;
@@ -53,19 +76,31 @@
 Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
 Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
 Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
+Landroid/content/ContentProvider;->attachInfoForTesting(Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V
+Landroid/content/ContentProvider;->getIContentProvider()Landroid/content/IContentProvider;
+Landroid/content/ContentProvider;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Landroid/content/pm/PathPermission;)V
+Landroid/content/ContentResolver;->registerContentObserver(Landroid/net/Uri;ZLandroid/database/ContentObserver;I)V
+Landroid/content/ContentValues;->getStringArrayList(Ljava/lang/String;)Ljava/util/ArrayList;
+Landroid/content/ContentValues;->putStringArrayList(Ljava/lang/String;Ljava/util/ArrayList;)V
 Landroid/content/Context;->getOpPackageName()Ljava/lang/String;
 Landroid/content/Context;->registerReceiverAsUser(Landroid/content/BroadcastReceiver;Landroid/os/UserHandle;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;)Landroid/content/Intent;
 Landroid/content/Context;->startActivityAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V
 Landroid/content/Context;->startServiceAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)Landroid/content/ComponentName;
 Landroid/content/ContextWrapper;->getThemeResId()I
+Landroid/content/Intent;->getExtra(Ljava/lang/String;)Ljava/lang/Object;
+Landroid/content/Intent;->getIBinderExtra(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/content/Intent;->resolveSystemService(Landroid/content/pm/PackageManager;I)Landroid/content/ComponentName;
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
 Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
 Landroid/database/sqlite/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
+Landroid/database/sqlite/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
 Landroid/graphics/AvoidXfermode;-><init>(IILandroid/graphics/AvoidXfermode$Mode;)V
 Landroid/graphics/Bitmap;->createGraphicBufferHandle()Landroid/graphics/GraphicBuffer;
 Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
@@ -74,17 +109,24 @@
 Landroid/graphics/drawable/Drawable;->isProjected()Z
 Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Landroid/hardware/display/DisplayManagerGlobal;->getInstance()Landroid/hardware/display/DisplayManagerGlobal;
+Landroid/hardware/display/DisplayManagerGlobal;->getRealDisplay(I)Landroid/view/Display;
 Landroid/hardware/location/GeofenceHardware;-><init>(Landroid/hardware/location/IGeofenceHardware;)V
 Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
-Landroid/location/IFusedProvider;->onFusedLocationHardwareChange(Landroid/hardware/location/IFusedLocationHardware;)V
 Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
 Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/Location;->setExtraLocation(Ljava/lang/String;Landroid/location/Location;)V
 Landroid/media/AudioManager;->registerAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V
 Landroid/media/AudioManager;->unregisterAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V
 Landroid/media/AudioSystem;->checkAudioFlinger()I
+Landroid/media/AudioSystem;->getForceUse(I)I
 Landroid/media/AudioSystem;->getParameters(Ljava/lang/String;)Ljava/lang/String;
+Landroid/media/AudioSystem;->setForceUse(II)I
 Landroid/media/AudioSystem;->setParameters(Ljava/lang/String;)I
 Landroid/media/MediaDrm$Certificate;->getContent()[B
 Landroid/media/MediaDrm$Certificate;->getWrappedPrivateKey()[B
@@ -103,14 +145,18 @@
 Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
 Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
 Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo;
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onError(I)V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStarted()V
 Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStopped()V
 Landroid/net/ConnectivityManager$PacketKeepalive;->stop()V
+Landroid/net/ConnectivityManager;->setAirplaneMode(Z)V
 Landroid/net/ConnectivityManager;->startNattKeepalive(Landroid/net/Network;ILandroid/net/ConnectivityManager$PacketKeepaliveCallback;Ljava/net/InetAddress;ILjava/net/InetAddress;)Landroid/net/ConnectivityManager$PacketKeepalive;
 Landroid/net/ConnectivityManager;->startUsingNetworkFeature(ILjava/lang/String;)I
 Landroid/net/ConnectivityManager;->stopUsingNetworkFeature(ILjava/lang/String;)I
+Landroid/net/ConnectivityManager;->tether(Ljava/lang/String;)I
+Landroid/net/ConnectivityManager;->untether(Ljava/lang/String;)I
 Landroid/net/DhcpResults;-><init>(Landroid/net/DhcpResults;)V
 Landroid/net/DhcpResults;-><init>(Landroid/net/StaticIpConfiguration;)V
 Landroid/net/DhcpResults;-><init>()V
@@ -138,6 +184,8 @@
 Landroid/net/LinkProperties;->addStackedLink(Landroid/net/LinkProperties;)Z
 Landroid/net/LinkProperties;->clear()V
 Landroid/net/LinkProperties;->compareProvisioning(Landroid/net/LinkProperties;Landroid/net/LinkProperties;)Landroid/net/LinkProperties$ProvisioningChange;
+Landroid/net/LinkProperties;->getAllInterfaceNames()Ljava/util/List;
+Landroid/net/LinkProperties;->getAllRoutes()Ljava/util/List;
 Landroid/net/LinkProperties;->getMtu()I
 Landroid/net/LinkProperties;->getStackedLinks()Ljava/util/List;
 Landroid/net/LinkProperties;->hasGlobalIPv6Address()Z
@@ -226,12 +274,23 @@
 Landroid/net/NetworkCapabilities;->hasSignalStrength()Z
 Landroid/net/NetworkCapabilities;->transportNamesOf([I)Ljava/lang/String;
 Landroid/net/Network;-><init>(I)V
+Landroid/net/Network;->netId:I
 Landroid/net/NetworkQuotaInfo;->getEstimatedBytes()J
 Landroid/net/NetworkQuotaInfo;->getHardLimitBytes()J
 Landroid/net/NetworkQuotaInfo;->getSoftLimitBytes()J
 Landroid/net/NetworkRequest$Builder;->setSignalStrength(I)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest;->networkCapabilities:Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkState;->network:Landroid/net/Network;
 Landroid/net/NetworkStats;->combineValues(Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats$Entry;->iface:Ljava/lang/String;
 Landroid/net/NetworkStats$Entry;-><init>()V
+Landroid/net/NetworkStats$Entry;->rxBytes:J
+Landroid/net/NetworkStats$Entry;->rxPackets:J
+Landroid/net/NetworkStats$Entry;->set:I
+Landroid/net/NetworkStats$Entry;->tag:I
+Landroid/net/NetworkStats$Entry;->txBytes:J
+Landroid/net/NetworkStats$Entry;->txPackets:J
+Landroid/net/NetworkStats$Entry;->uid:I
 Landroid/net/NetworkStatsHistory$Entry;->txBytes:J
 Landroid/net/NetworkStatsHistory;->getStart()J
 Landroid/net/NetworkStatsHistory;->getValues(JJLandroid/net/NetworkStatsHistory$Entry;)Landroid/net/NetworkStatsHistory$Entry;
@@ -245,8 +304,10 @@
 Landroid/net/NetworkUtils;->protectFromVpn(Ljava/io/FileDescriptor;)Z
 Landroid/net/RouteInfo;->hasGateway()Z
 Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;Ljava/lang/String;)V
+Landroid/net/RouteInfo;->selectBestRoute(Ljava/util/Collection;Ljava/net/InetAddress;)Landroid/net/RouteInfo;
 Landroid/net/SntpClient;->getNtpTime()J
-Landroid/net/SntpClient;->requestTime(Ljava/lang/String;I)Z
+Landroid/net/SntpClient;->getNtpTimeReference()J
+Landroid/net/SntpClient;->getRoundTripTime()J
 Landroid/net/StaticIpConfiguration;->dnsServers:Ljava/util/ArrayList;
 Landroid/net/StaticIpConfiguration;->domains:Ljava/lang/String;
 Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
@@ -268,27 +329,36 @@
 Landroid/os/BatteryStats$HistoryItem;-><init>()V
 Landroid/os/BatteryStats$HistoryItem;->states:I
 Landroid/os/BatteryStats$HistoryItem;->time:J
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
 Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
 Landroid/os/BatteryStats$Uid;-><init>()V
 Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
 Landroid/os/Handler;->getMain()Landroid/os/Handler;
 Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
 Landroid/os/HwBinder;->reportSyspropChanged()V
 Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
 Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
 Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
 Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
 Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
 Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
 Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/IPowerManager;->goToSleep(JII)V
 Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
 Landroid/os/IRemoteCallback$Stub;-><init>()V
 Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
 Landroid/os/Parcel;->readBlob()[B
 Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
 Landroid/os/Parcel;->writeBlob([B)V
+Landroid/os/PowerManager;->goToSleep(J)V
 Landroid/os/PowerManager;->isScreenBrightnessBoosted()Z
 Landroid/os/Registrant;->clear()V
 Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
@@ -303,10 +373,35 @@
 Landroid/os/Registrant;->notifyRegistrant()V
 Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
 Landroid/os/ServiceSpecificException;->errorCode:I
+Landroid/os/storage/DiskInfo;->getId()Ljava/lang/String;
+Landroid/os/storage/StorageEventListener;-><init>()V
+Landroid/os/storage/StorageManager;->findVolumeById(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
+Landroid/os/storage/StorageManager;->from(Landroid/content/Context;)Landroid/os/storage/StorageManager;
+Landroid/os/storage/StorageManager;->registerListener(Landroid/os/storage/StorageEventListener;)V
+Landroid/os/storage/StorageManager;->unregisterListener(Landroid/os/storage/StorageEventListener;)V
+Landroid/os/storage/StorageVolume;->getId()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getId()Ljava/lang/String;
 Landroid/os/SystemProperties;->reportSyspropChanged()V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
+Landroid/os/UserHandle;->isSameApp(II)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isAdminUser()Z
 Landroid/print/PrintDocumentAdapter$LayoutResultCallback;-><init>()V
 Landroid/print/PrintDocumentAdapter$WriteResultCallback;-><init>()V
 Landroid/provider/CalendarContract$Events;->PROVIDER_WRITABLE_COLUMNS:[Ljava/lang/String;
+Landroid/provider/ContactsContract$CommonDataKinds$Phone;->getDisplayLabel(Landroid/content/Context;ILjava/lang/CharSequence;)Ljava/lang/CharSequence;
+Landroid/provider/Settings$Global;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Settings$Secure;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
+Landroid/provider/Telephony$Mms;->isEmailAddress(Ljava/lang/String;)Z
+Landroid/provider/Telephony$Sms$Draft;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Outbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZJ)Landroid/net/Uri;
+Landroid/R$styleable;->CheckBoxPreference:[I
+Landroid/service/dreams/DreamService;->canDoze()Z
+Landroid/service/dreams/DreamService;->isDozing()Z
+Landroid/service/dreams/DreamService;->startDozing()V
+Landroid/service/dreams/DreamService;->stopDozing()V
 Landroid/service/vr/VrListenerService;->onCurrentVrActivityChanged(Landroid/content/ComponentName;ZI)V
 Landroid/system/NetlinkSocketAddress;-><init>(II)V
 Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
@@ -316,6 +411,11 @@
 Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
 Landroid/system/PacketSocketAddress;-><init>(I[B)V
 Landroid/system/PacketSocketAddress;-><init>(SI)V
+Landroid/telecom/ParcelableCall;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/telecom/ParcelableCall;->getConnectTimeMillis()J
+Landroid/telecom/ParcelableCall;->getDisconnectCause()Landroid/telecom/DisconnectCause;
+Landroid/telecom/ParcelableCall;->getHandle()Landroid/net/Uri;
+Landroid/telecom/ParcelableCall;->getId()Ljava/lang/String;
 Landroid/telecom/TelecomManager;->from(Landroid/content/Context;)Landroid/telecom/TelecomManager;
 Landroid/telecom/VideoProfile$CameraCapabilities;-><init>(IIZF)V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
@@ -329,6 +429,14 @@
 Landroid/telephony/ims/ImsReasonInfo;-><init>(IILjava/lang/String;)V
 Landroid/telephony/ims/ImsReasonInfo;-><init>(II)V
 Landroid/telephony/ims/ImsStreamMediaProfile;-><init>()V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/PhoneNumberUtils;->formatNumber(Ljava/lang/String;I)Ljava/lang/String;
 Landroid/telephony/PhoneNumberUtils;->isEmergencyNumber(ILjava/lang/String;)Z
 Landroid/telephony/PhoneNumberUtils;->isPotentialEmergencyNumber(ILjava/lang/String;)Z
 Landroid/telephony/PhoneNumberUtils;->isPotentialLocalEmergencyNumber(Landroid/content/Context;ILjava/lang/String;)Z
@@ -342,12 +450,20 @@
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
 Landroid/telephony/Rlog;->i(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/telephony/ServiceState;->getDataRegState()I
 Landroid/telephony/ServiceState;->getDataRoaming()Z
 Landroid/telephony/ServiceState;->getRilDataRadioTechnology()I
+Landroid/telephony/ServiceState;->getVoiceNetworkType()I
 Landroid/telephony/ServiceState;->getVoiceRegState()I
 Landroid/telephony/ServiceState;->isCdma(I)Z
 Landroid/telephony/ServiceState;->isEmergencyOnly()Z
+Landroid/telephony/ServiceState;->isGsm(I)Z
 Landroid/telephony/ServiceState;->mergeServiceStates(Landroid/telephony/ServiceState;Landroid/telephony/ServiceState;)Landroid/telephony/ServiceState;
+Landroid/telephony/ServiceState;->rilRadioTechnologyToString(I)Ljava/lang/String;
+Landroid/telephony/SubscriptionInfo;->setDisplayName(Ljava/lang/CharSequence;)V
+Landroid/telephony/SubscriptionInfo;->setIconTint(I)V
+Landroid/telephony/SubscriptionManager;->clearDefaultsForInactiveSubIds()V
+Landroid/telephony/SubscriptionManager;->getDefaultVoicePhoneId()I
 Landroid/telephony/SubscriptionManager;->getResourcesForSubId(Landroid/content/Context;I)Landroid/content/res/Resources;
 Landroid/telephony/SubscriptionManager;->isActiveSubId(I)Z
 Landroid/telephony/SubscriptionManager;->isUsableSubIdValue(I)Z
@@ -355,9 +471,18 @@
 Landroid/telephony/SubscriptionManager;->isValidSlotIndex(I)Z
 Landroid/telephony/SubscriptionManager;->isValidSubscriptionId(I)Z
 Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;II)V
+Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;I)V
+Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
+Landroid/telephony/SubscriptionManager;->setDisplayName(Ljava/lang/String;IJ)I
+Landroid/telephony/SubscriptionManager;->setIconTint(II)I
 Landroid/telephony/TelephonyManager;->getIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;I)I
+Landroid/telephony/TelephonyManager;->getNetworkTypeName()Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getVoiceMessageCount()I
 Landroid/telephony/TelephonyManager;->getVoiceNetworkType(I)I
+Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDA:Landroid/telephony/TelephonyManager$MultiSimVariants;
+Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDS:Landroid/telephony/TelephonyManager$MultiSimVariants;
 Landroid/telephony/TelephonyManager;->putIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;II)Z
+Landroid/text/TextUtils;->isPrintableAsciiOnly(Ljava/lang/CharSequence;)Z
 Landroid/util/FloatMath;->ceil(F)F
 Landroid/util/FloatMath;->cos(F)F
 Landroid/util/FloatMath;->exp(F)F
@@ -374,6 +499,9 @@
 Landroid/util/LongArray;->get(I)J
 Landroid/util/LongArray;-><init>()V
 Landroid/util/LongArray;->size()I
+Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
+Landroid/util/Slog;->println(ILjava/lang/String;Ljava/lang/String;)I
 Landroid/util/Slog;->wtf(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/view/AppTransitionAnimationSpec;-><init>(ILandroid/graphics/GraphicBuffer;Landroid/graphics/Rect;)V
 Landroid/view/BatchedInputEventReceiver;-><init>(Landroid/view/InputChannel;Landroid/os/Looper;Landroid/view/Choreographer;)V
@@ -525,6 +653,19 @@
 Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
 Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/internal/app/AlertController$AlertParams;->mIconId:I
+Lcom/android/internal/app/AlertController$AlertParams;->mMessage:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonListener:Landroid/content/DialogInterface$OnClickListener;
+Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonText:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonListener:Landroid/content/DialogInterface$OnClickListener;
+Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonText:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mTitle:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController$AlertParams;->mView:Landroid/view/View;
+Lcom/android/internal/app/AlertController;->getButton(I)Landroid/widget/Button;
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
+Lcom/android/internal/content/PackageMonitor;->register(Landroid/content/Context;Landroid/os/Looper;Landroid/os/UserHandle;Z)V
+Lcom/android/internal/content/PackageMonitor;->unregister()V
 Lcom/android/internal/location/ILocationProvider;->disable()V
 Lcom/android/internal/location/ILocationProvider;->enable()V
 Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
@@ -532,6 +673,11 @@
 Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
 Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
 Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ProviderRequest;-><init>()V
+Lcom/android/internal/location/ProviderRequest;->interval:J
+Lcom/android/internal/location/ProviderRequest;->locationRequests:Ljava/util/List;
+Lcom/android/internal/location/ProviderRequest;->reportLocation:Z
 Lcom/android/internal/os/BatteryStatsImpl;->getDischargeCurrentLevel()I
 Lcom/android/internal/os/BatteryStatsImpl;->getDischargeStartLevel()I
 Lcom/android/internal/os/BatteryStatsImpl;->getPhoneOnTime(JI)J
@@ -541,7 +687,22 @@
 Lcom/android/internal/os/BatteryStatsImpl;->getWifiOnTime(JI)J
 Lcom/android/internal/os/SomeArgs;->obtain()Lcom/android/internal/os/SomeArgs;
 Lcom/android/internal/os/SomeArgs;->recycle()V
+Lcom/android/internal/R$styleable;->NumberPicker:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
+Lcom/android/internal/telephony/GsmAlphabet;->gsm7BitPackedToString([BII)Ljava/lang/String;
+Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm7BitPacked(Ljava/lang/String;)[B
 Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
+Lcom/android/internal/telephony/OperatorInfo;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaLong()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaShort()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getOperatorNumeric()Ljava/lang/String;
+Lcom/android/internal/telephony/OperatorInfo;->getState()Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/telephony/OperatorInfo;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/OperatorInfo$State;->CURRENT:Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/telephony/OperatorInfo$State;->FORBIDDEN:Lcom/android/internal/telephony/OperatorInfo$State;
+Lcom/android/internal/util/AsyncChannel;->connect(Landroid/content/Context;Landroid/os/Handler;Landroid/os/Messenger;)V
+Lcom/android/internal/util/AsyncChannel;-><init>()V
+Lcom/android/internal/util/AsyncChannel;->sendMessage(Landroid/os/Message;)V
 Lcom/android/internal/util/IndentingPrintWriter;->decreaseIndent()Lcom/android/internal/util/IndentingPrintWriter;
 Lcom/android/internal/util/IndentingPrintWriter;->increaseIndent()Lcom/android/internal/util/IndentingPrintWriter;
 Lcom/android/internal/util/IndentingPrintWriter;-><init>(Ljava/io/Writer;Ljava/lang/String;)V
@@ -550,3 +711,4 @@
 Ljava/lang/System;->arraycopy([BI[BII)V
 Ljava/net/Inet4Address;->ALL:Ljava/net/InetAddress;
 Ljava/net/Inet4Address;->ANY:Ljava/net/InetAddress;
+Ljava/net/Inet6Address;->ANY:Ljava/net/InetAddress;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 0b10a35..452225c 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.accessibilityservice;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
@@ -50,8 +52,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -410,6 +410,15 @@
     public int flags;
 
     /**
+     * Whether or not the service has crashed and is awaiting restart. Only valid from {@link
+     * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)},
+     * because that is populated from the internal list of running services.
+     *
+     * @hide
+     */
+    public boolean crashed;
+
+    /**
      * The component name the accessibility service.
      */
     private ComponentName mComponentName;
@@ -757,6 +766,7 @@
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
         parcel.writeInt(flags);
+        parcel.writeInt(crashed ? 1 : 0);
         parcel.writeParcelable(mComponentName, flagz);
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
@@ -773,6 +783,7 @@
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
         flags = parcel.readInt();
+        crashed = parcel.readInt() != 0;
         mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a1a10a5..3696eae 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -657,13 +657,13 @@
  * <a name="ProcessLifecycle"></a>
  * <h3>Process Lifecycle</h3>
  *
- * <p>The Android system attempts to keep application process around for as
+ * <p>The Android system attempts to keep an application process around for as
  * long as possible, but eventually will need to remove old processes when
- * memory runs low.  As described in <a href="#ActivityLifecycle">Activity
+ * memory runs low. As described in <a href="#ActivityLifecycle">Activity
  * Lifecycle</a>, the decision about which process to remove is intimately
- * tied to the state of the user's interaction with it.  In general, there
+ * tied to the state of the user's interaction with it. In general, there
  * are four states a process can be in based on the activities running in it,
- * listed here in order of importance.  The system will kill less important
+ * listed here in order of importance. The system will kill less important
  * processes (the last ones) before it resorts to killing more important
  * processes (the first ones).
  *
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2752fa9..db9d923 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -385,4 +385,10 @@
      * Returns a list that contains the memory stats for currently running processes.
      */
     public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
+
+    /**
+     * This enforces {@code func} can only be called if either the caller is Recents activity or
+     * has {@code permission}.
+     */
+    public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8dd0f42..baf2d60 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
+import android.app.servertransaction.TransactionExecutorHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -64,6 +67,7 @@
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
@@ -143,7 +147,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.server.am.proto.MemInfoDumpProto;
+import com.android.server.am.MemInfoDumpProto;
 
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.CloseGuard;
@@ -519,6 +523,10 @@
             return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
+        public boolean isVisibleFromServer() {
+            return activity != null && activity.mVisibleFromServer;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -1796,6 +1804,7 @@
                         // message is handled.
                         transaction.recycle();
                     }
+                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
             }
             Object obj = msg.obj;
@@ -4033,9 +4042,11 @@
         r.setState(ON_PAUSE);
     }
 
+    /** Called from {@link LocalActivityManager}. */
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        performStopActivityInner(r, null, false, saveState, reason);
+        performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
+                false /* finalStateRequest */, reason);
     }
 
     private static final class ProviderRefCount {
@@ -4067,9 +4078,16 @@
      * it the result when it is done, but the window may still be visible.
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibility changes.
+     * @param r Target activity client record.
+     * @param info Action that will report activity stop to server.
+     * @param keepShown Flag indicating whether the activity is still shown.
+     * @param saveState Flag indicating whether the activity state should be saved.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
      */
     private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
-            boolean saveState, String reason) {
+            boolean saveState, boolean finalStateRequest, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
             if (!keepShown && r.stopped) {
@@ -4079,11 +4097,13 @@
                     // if the activity isn't resumed.
                     return;
                 }
-                RuntimeException e = new RuntimeException(
-                        "Performing stop of activity that is already stopped: "
-                        + r.intent.getComponent().toShortString());
-                Slog.e(TAG, e.getMessage(), e);
-                Slog.e(TAG, r.getStateString());
+                if (!finalStateRequest) {
+                    final RuntimeException e = new RuntimeException(
+                            "Performing stop of activity that is already stopped: "
+                                    + r.intent.getComponent().toShortString());
+                    Slog.e(TAG, e.getMessage(), e);
+                    Slog.e(TAG, r.getStateString());
+                }
             }
 
             // One must first be paused before stopped...
@@ -4176,12 +4196,13 @@
 
     @Override
     public void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions, String reason) {
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
         final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
         final StopInfo stopInfo = new StopInfo();
-        performStopActivityInner(r, stopInfo, show, true, reason);
+        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
+                reason);
 
         if (localLOGV) Slog.v(
             TAG, "Finishing stop of " + r + ": show=" + show
@@ -4233,7 +4254,8 @@
         }
 
         if (!show && !r.stopped) {
-            performStopActivityInner(r, null, show, false, "handleWindowVisibility");
+            performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
+                    false /* finalStateRequest */, "handleWindowVisibility");
         } else if (show && r.stopped) {
             // If we are getting ready to gc after going to the background, well
             // we are back active so skip it.
@@ -4709,14 +4731,25 @@
             return;
         }
 
-        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
-        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
-                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
-                r.overrideConfig, "handleRelaunchActivityLocally");
 
-        // Restore back to the previous state before relaunch if needed.
-        if (prevState != r.getLifecycleState()) {
-            mTransactionExecutor.cycleToPath(r, prevState);
+        // Initialize a relaunch request.
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                r.createdConfig != null ? r.createdConfig : mConfiguration,
+                r.overrideConfig);
+        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
+                mergedConfiguration, r.mPreserveWindow);
+        // Make sure to match the existing lifecycle state in the end of the transaction.
+        final ActivityLifecycleItem lifecycleRequest =
+                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
+        // Schedule the transaction.
+        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        transaction.addCallback(activityRelaunchItem);
+        transaction.setLifecycleStateRequest(lifecycleRequest);
+        try {
+            mAppThread.scheduleTransaction(transaction);
+        } catch (RemoteException e) {
+            // Local scheduling
         }
     }
 
@@ -5551,6 +5584,13 @@
 
         Message.updateCheckRecycle(data.appInfo.targetSdkVersion);
 
+        // Prior to P, internal calls to decode Bitmaps used BitmapFactory,
+        // which may scale up to account for density. In P, we switched to
+        // ImageDecoder, which skips the upscale to save memory. ImageDecoder
+        // needs to still scale up in older apps, in case they rely on the
+        // size of the Bitmap without considering its density.
+        ImageDecoder.sApiLevel = data.appInfo.targetSdkVersion;
+
         /*
          * Before spawning a new process, reset the time zone to be the system time zone.
          * This needs to be done because the system time zone could have changed after the
@@ -5652,6 +5692,7 @@
         // Allow application-generated systrace messages if we're debuggable.
         boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         Trace.setAppTracingAllowed(isAppDebuggable);
+        ThreadedRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
         if (isAppDebuggable && data.enableBinderTracking) {
             Binder.enableTracing();
         }
@@ -5710,6 +5751,8 @@
             } finally {
                 StrictMode.setThreadPolicyMask(oldMask);
             }
+        } else {
+            ThreadedRenderer.setIsolatedProcess(true);
         }
 
         // If we use profiles, setup the dex reporter to notify package manager
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 5d0143a..7032a2f 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -27,6 +27,7 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.MotionEvent;
@@ -308,8 +309,14 @@
             return;
         }
 
-        mInputForwarder = InputManager.getInstance().createInputForwarder(
-                mVirtualDisplay.getDisplay().getDisplayId());
+        final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        try {
+            wm.dontOverrideDisplayInfo(displayId);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
         mTaskStackListener = new TaskBackgroundChangeListener();
         try {
             mActivityManager.registerTaskStackListener(mTaskStackListener);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 14edd31..ea8c71c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -270,8 +270,10 @@
     public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
     /** @hide Continue handover of a call from another app */
     public static final int OP_ACCEPT_HANDOVER = 74;
+    /** @hide Create and Manage IPsec Tunnels */
+    public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
     /** @hide */
-    public static final int _NUM_OP = 75;
+    public static final int _NUM_OP = 76;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -499,13 +501,17 @@
     public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_CHANGE_WIFI_STATE = "change_wifi_state";
+    public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_REQUEST_DELETE_PACKAGES = "request_delete_packages";
+    public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "bind_accessibility_service";
+    public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE =
+            "android:bind_accessibility_service";
+    /** @hide */
+    @SystemApi @TestApi
+    public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
 
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
@@ -640,6 +646,7 @@
             OP_REQUEST_DELETE_PACKAGES,
             OP_BIND_ACCESSIBILITY_SERVICE,
             OP_ACCEPT_HANDOVER,
+            OP_MANAGE_IPSEC_TUNNELS,
     };
 
     /**
@@ -721,6 +728,7 @@
             OPSTR_REQUEST_DELETE_PACKAGES,
             OPSTR_BIND_ACCESSIBILITY_SERVICE,
             OPSTR_ACCEPT_HANDOVER,
+            OPSTR_MANAGE_IPSEC_TUNNELS,
     };
 
     /**
@@ -803,6 +811,7 @@
             "REQUEST_DELETE_PACKAGES",
             "BIND_ACCESSIBILITY_SERVICE",
             "ACCEPT_HANDOVER",
+            "MANAGE_IPSEC_TUNNELS",
     };
 
     /**
@@ -885,6 +894,7 @@
             Manifest.permission.REQUEST_DELETE_PACKAGES,
             Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
             Manifest.permission.ACCEPT_HANDOVER,
+            null, // no permission for OP_MANAGE_IPSEC_TUNNELS
     };
 
     /**
@@ -968,6 +978,7 @@
             null, // REQUEST_DELETE_PACKAGES
             null, // OP_BIND_ACCESSIBILITY_SERVICE
             null, // ACCEPT_HANDOVER
+            null, // MANAGE_IPSEC_TUNNELS
     };
 
     /**
@@ -1050,6 +1061,7 @@
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
             false, // ACCEPT_HANDOVER
+            false, // MANAGE_IPSEC_HANDOVERS
     };
 
     /**
@@ -1131,6 +1143,7 @@
             AppOpsManager.MODE_ALLOWED,  // REQUEST_DELETE_PACKAGES
             AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
             AppOpsManager.MODE_ALLOWED,  // ACCEPT_HANDOVER
+            AppOpsManager.MODE_ERRORED,  // MANAGE_IPSEC_TUNNELS
     };
 
     /**
@@ -1216,6 +1229,7 @@
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
             false, // ACCEPT_HANDOVER
+            false, // MANAGE_IPSEC_TUNNELS
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 21fb18a..fb8ded1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.UserIdInt;
 import android.annotation.XmlRes;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -69,6 +70,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -1031,19 +1033,25 @@
     }
 
     @Override
-    public ResolveInfo resolveService(Intent intent, int flags) {
+    public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId) {
         try {
             return mPM.resolveService(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                mContext.getUserId());
+                userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    public ResolveInfo resolveService(Intent intent, int flags) {
+        return resolveServiceAsUser(intent, flags, mContext.getUserId());
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
         try {
@@ -2144,16 +2152,42 @@
     }
 
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras,
+            String dialogMessage) {
+        // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
         try {
-            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
+            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
+                    launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+        try {
+            return mPM.getPackageSuspendedAppExtras(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public PersistableBundle getSuspendedPackageAppExtras() {
+        return getSuspendedPackageAppExtras(mContext.getOpPackageName());
+    }
+
+    @Override
+    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
+        try {
+            mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         try {
             return mPM.isPackageSuspendedForUser(packageName, userId);
@@ -2164,6 +2198,17 @@
 
     /** @hide */
     @Override
+    public boolean isPackageSuspended(String packageName) {
+        return isPackageSuspendedForUser(packageName, mContext.getUserId());
+    }
+
+    @Override
+    public boolean isPackageSuspended() {
+        return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
+    }
+
+    /** @hide */
+    @Override
     public void setApplicationCategoryHint(String packageName, int categoryHint) {
         try {
             mPM.setApplicationCategoryHint(packageName, categoryHint,
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 206495d..961bca2 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -78,9 +78,19 @@
     public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
             boolean isForward, String reason);
 
-    /** Stop the activity. */
+    /**
+     * Stop the activity.
+     * @param token Target activity token.
+     * @param show Flag indicating whether activity is still shown.
+     * @param configChanges Activity configuration changes.
+     * @param pendingActions Pending actions to be used on this or later stages of activity
+     *                       transaction.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
+     */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions, String reason);
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
 
     /** Report that activity was stopped to server. */
     public abstract void reportStop(PendingTransactionActions pendingActions);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2ac3e23..d3c1e99 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -89,6 +89,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -2599,6 +2600,80 @@
     };
 
     /**
+     * @hide
+     */
+    public static boolean areActionsVisiblyDifferent(Notification first, Notification second) {
+        Notification.Action[] firstAs = first.actions;
+        Notification.Action[] secondAs = second.actions;
+        if (firstAs == null && secondAs != null || firstAs != null && secondAs == null) {
+            return true;
+        }
+        if (firstAs != null && secondAs != null) {
+            if (firstAs.length != secondAs.length) {
+                return true;
+            }
+            for (int i = 0; i < firstAs.length; i++) {
+                if (!Objects.equals(firstAs[i].title, secondAs[i].title)) {
+                    return true;
+                }
+                RemoteInput[] firstRs = firstAs[i].getRemoteInputs();
+                RemoteInput[] secondRs = secondAs[i].getRemoteInputs();
+                if (firstRs == null) {
+                    firstRs = new RemoteInput[0];
+                }
+                if (secondRs == null) {
+                    secondRs = new RemoteInput[0];
+                }
+                if (firstRs.length != secondRs.length) {
+                    return true;
+                }
+                for (int j = 0; j < firstRs.length; j++) {
+                    if (!Objects.equals(firstRs[j].getLabel(), secondRs[j].getLabel())) {
+                        return true;
+                    }
+                    CharSequence[] firstCs = firstRs[j].getChoices();
+                    CharSequence[] secondCs = secondRs[j].getChoices();
+                    if (firstCs == null) {
+                        firstCs = new CharSequence[0];
+                    }
+                    if (secondCs == null) {
+                        secondCs = new CharSequence[0];
+                    }
+                    if (firstCs.length != secondCs.length) {
+                        return true;
+                    }
+                    for (int k = 0; k < firstCs.length; k++) {
+                        if (!Objects.equals(firstCs[k], secondCs[k])) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
+        if (first.getStyle() == null) {
+            return second.getStyle() != null;
+        }
+        if (second.getStyle() == null) {
+            return true;
+        }
+        return first.getStyle().areNotificationsVisiblyDifferent(second.getStyle());
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areRemoteViewsChanged(Builder first, Builder second) {
+        return !first.usesStandardHeader() || !second.usesStandardHeader();
+    }
+
+    /**
      * Parcelling creates multiple copies of objects in {@code extras}. Fix them.
      * <p>
      * For backwards compatibility {@code extras} holds some references to "real" member data such
@@ -3043,7 +3118,6 @@
         private int mActionBarColor = COLOR_INVALID;
         private int mBackgroundColor = COLOR_INVALID;
         private int mForegroundColor = COLOR_INVALID;
-        private int mBackgroundColorHint = COLOR_INVALID;
         /**
          * A temporary location where actions are stored. If != null the view originally has action
          * but doesn't have any for this inflation.
@@ -4039,6 +4113,13 @@
         }
 
         /**
+         * Returns the style set by {@link #setStyle(Style)}.
+         */
+        public Style getStyle() {
+            return mStyle;
+        }
+
+        /**
          * Specify the value of {@link #visibility}.
          *
          * @return The same Builder.
@@ -4305,8 +4386,7 @@
                             backgroundColor);
                     mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
                             backgroundColor);
-                    if (backgroundColor != COLOR_DEFAULT
-                            && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
+                    if (backgroundColor != COLOR_DEFAULT && isColorized()) {
                         mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
                                 mPrimaryTextColor, backgroundColor, 4.5);
                         mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
@@ -4513,21 +4593,13 @@
         }
 
         private void bindExpandButton(RemoteViews contentView) {
-            int color = getPrimaryHighlightColor();
+            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
             contentView.setDrawableTint(R.id.expand_button, false, color,
                     PorterDuff.Mode.SRC_ATOP);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
                     color);
         }
 
-        /**
-         * @return the color that is used as the first primary highlight color. This is applied
-         * in several places like the action buttons or the app name in the header.
-         */
-        private int getPrimaryHighlightColor() {
-            return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
-        }
-
         private void bindHeaderChronometerAndTime(RemoteViews contentView) {
             if (showsTimeOrChronometer()) {
                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
@@ -4624,7 +4696,7 @@
                 setTextViewColorPrimary(contentView, R.id.app_name_text);
             } else {
                 contentView.setTextColor(R.id.app_name_text,
-                        ambient ? resolveAmbientColor() : resolveContrastColor());
+                        ambient ? resolveAmbientColor() : getSecondaryTextColor());
             }
         }
 
@@ -5152,7 +5224,14 @@
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
                 boolean ambient) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
-            int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
+            int color;
+            if (ambient) {
+                color = resolveAmbientColor();
+            } else if (isColorized()) {
+                color = getPrimaryTextColor();
+            } else {
+                color = resolveContrastColor();
+            }
             if (colorable) {
                 contentView.setDrawableTint(R.id.icon, false, color,
                         PorterDuff.Mode.SRC_ATOP);
@@ -5188,14 +5267,11 @@
             }
 
             int color;
-            int background = mBackgroundColorHint;
-            if (mBackgroundColorHint == COLOR_INVALID) {
-                background = mContext.getColor(
-                        com.android.internal.R.color.notification_material_background_color);
-            }
+            int background = mContext.getColor(
+                    com.android.internal.R.color.notification_material_background_color);
             if (mN.color == COLOR_DEFAULT) {
                 ensureColors();
-                color = mSecondaryTextColor;
+                color = NotificationColorUtil.resolveDefaultColor(mContext, background);
             } else {
                 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
                         background, mInNightMode);
@@ -5435,8 +5511,7 @@
             if (isColorized()) {
                 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
             } else {
-                return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
-                        : COLOR_DEFAULT;
+                return COLOR_DEFAULT;
             }
         }
 
@@ -5473,18 +5548,6 @@
         }
 
         /**
-         * Sets the background color for this notification to be a different one then the default.
-         * This is mainly used to calculate contrast and won't necessarily be applied to the
-         * background.
-         *
-         * @hide
-         */
-        public void setBackgroundColorHint(int backgroundColor) {
-            mBackgroundColorHint = backgroundColor;
-        }
-
-
-        /**
          * Forces all styled remoteViews to be built from scratch and not use any cached
          * RemoteViews.
          * This is needed for legacy apps that are baking in their remoteviews into the
@@ -5495,6 +5558,24 @@
         public void setRebuildStyledRemoteViews(boolean rebuild) {
             mRebuildStyledRemoteViews = rebuild;
         }
+
+        /**
+         * Get the text that should be displayed in the statusBar when heads upped. This is
+         * usually just the app name, but may be different depending on the style.
+         *
+         * @param publicMode If true, return a text that is safe to display in public.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText(boolean publicMode) {
+            if (mStyle != null && !publicMode) {
+                CharSequence text = mStyle.getHeadsUpStatusBarText();
+                if (!TextUtils.isEmpty(text)) {
+                    return text;
+                }
+            }
+            return loadHeaderAppName();
+        }
     }
 
     /**
@@ -5867,6 +5948,21 @@
          */
         public void validate(Context context) {
         }
+
+        /**
+         * @hide
+         */
+        public abstract boolean areNotificationsVisiblyDifferent(Style other);
+
+        /**
+         * @return the the text that should be displayed in the statusBar when heads-upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText() {
+            return null;
+        }
     }
 
     /**
@@ -5920,6 +6016,13 @@
         }
 
         /**
+         * @hide
+         */
+        public Bitmap getBigPicture() {
+            return mPicture;
+        }
+
+        /**
          * Provide the bitmap to be used as the payload for the BigPicture notification.
          */
         public BigPictureStyle bigPicture(Bitmap b) {
@@ -6059,6 +6162,18 @@
         public boolean hasSummaryInHeader() {
             return false;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigPictureStyle otherS = (BigPictureStyle) other;
+            return !Objects.equals(getBigPicture(), otherS.getBigPicture());
+        }
     }
 
     /**
@@ -6122,6 +6237,13 @@
         /**
          * @hide
          */
+        public CharSequence getBigText() {
+            return mBigText;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -6191,6 +6313,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigTextStyle newS = (BigTextStyle) other;
+            return !Objects.equals(getBigText(), newS.getBigText());
+        }
+
         static void applyBigTextContentView(Builder builder,
                 RemoteViews contentView, CharSequence bigTextText) {
             contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
@@ -6278,6 +6412,23 @@
         }
 
         /**
+         * @return the the text that should be displayed in the statusBar when heads upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        @Override
+        public CharSequence getHeadsUpStatusBarText() {
+            CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
+                    ? super.mBigContentTitle
+                    : mConversationTitle;
+            if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
+                return conversationTitle;
+            }
+            return null;
+        }
+
+        /**
          * @return the user to be displayed for any replies sent by the user
          */
         public Person getUser() {
@@ -6533,6 +6684,58 @@
             return remoteViews;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            MessagingStyle newS = (MessagingStyle) other;
+            List<MessagingStyle.Message> oldMs = getMessages();
+            List<MessagingStyle.Message> newMs = newS.getMessages();
+
+            if (oldMs == null) {
+                oldMs = new ArrayList<>();
+            }
+            if (newMs == null) {
+                newMs = new ArrayList<>();
+            }
+
+            int n = oldMs.size();
+            if (n != newMs.size()) {
+                return true;
+            }
+            for (int i = 0; i < n; i++) {
+                MessagingStyle.Message oldM = oldMs.get(i);
+                MessagingStyle.Message newM = newMs.get(i);
+                if (!Objects.equals(oldM.getText(), newM.getText())) {
+                    return true;
+                }
+                if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
+                    return true;
+                }
+                CharSequence oldSender = oldM.getSenderPerson() == null ? oldM.getSender()
+                        : oldM.getSenderPerson().getName();
+                CharSequence newSender = newM.getSenderPerson() == null ? newM.getSender()
+                        : newM.getSenderPerson().getName();
+                if (!Objects.equals(oldSender, newSender)) {
+                    return true;
+                }
+
+                String oldKey = oldM.getSenderPerson() == null
+                        ? null : oldM.getSenderPerson().getKey();
+                String newKey = newM.getSenderPerson() == null
+                        ? null : newM.getSenderPerson().getKey();
+                if (!Objects.equals(oldKey, newKey)) {
+                    return true;
+                }
+                // Other fields (like timestamp) intentionally excluded
+            }
+            return false;
+        }
+
         private Message findLatestIncomingMessage() {
             return findLatestIncomingMessage(mMessages);
         }
@@ -6964,6 +7167,13 @@
         /**
          * @hide
          */
+        public ArrayList<CharSequence> getLines() {
+            return mTexts;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -7042,6 +7252,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            InboxStyle newS = (InboxStyle) other;
+            return !Objects.equals(getLines(), newS.getLines());
+        }
+
         private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
             int endMargin = 0;
             if (first) {
@@ -7205,6 +7427,18 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // All fields to compare are on the Notification object
+            return false;
+        }
+
         private RemoteViews generateMediaActionButton(Action action, int color) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
@@ -7245,8 +7479,7 @@
                     }
 
                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
-                    final RemoteViews button = generateMediaActionButton(action,
-                            getPrimaryHighlightColor());
+                    final RemoteViews button = generateMediaActionButton(action, getActionColor());
                     view.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
@@ -7260,8 +7493,9 @@
             return view;
         }
 
-        private int getPrimaryHighlightColor() {
-            return mBuilder.getPrimaryHighlightColor();
+        private int getActionColor() {
+            return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
+                    : mBuilder.resolveContrastColor();
         }
 
         private RemoteViews makeMediaBigContentView() {
@@ -7281,7 +7515,7 @@
                 big.removeAllViews(com.android.internal.R.id.media_actions);
                 for (int i = 0; i < actionCount; i++) {
                     final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
-                            getPrimaryHighlightColor());
+                            getActionColor());
                     big.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
@@ -7414,6 +7648,18 @@
             }
             remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
     }
 
     /**
@@ -7504,6 +7750,18 @@
             return makeBigContentViewWithCustomContent(customRemoteView);
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
+
         private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
                 RemoteViews customContent) {
             if (customContent != null) {
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index b7100e6..6feb38e 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
@@ -25,6 +26,8 @@
 import android.os.Parcelable;
 import android.util.ArraySet;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -74,9 +77,14 @@
     private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
             "android.remoteinput.dataTypeResultsData";
 
-    /** Extra added to a clip data intent object identifying the source of the results. */
+    /** Extra added to a clip data intent object identifying the {@link Source} of the results. */
     private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource";
 
+    /** @hide */
+    @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_FREE_FORM_INPUT, SOURCE_CHOICE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Source {}
+
     /** The user manually entered the data. */
     public static final int SOURCE_FREE_FORM_INPUT = 0;
 
@@ -437,10 +445,9 @@
      *
      * @param intent The intent to add remote input source to. The {@link ClipData}
      *               field of the intent will be modified to contain the source.
-     *               field of the intent will be modified to contain the source.
      * @param source The source of the results.
      */
-    public static void setResultsSource(Intent intent, int source) {
+    public static void setResultsSource(Intent intent, @Source int source) {
         Intent clipDataIntent = getClipDataIntentFromIntent(intent);
         if (clipDataIntent == null) {
             clipDataIntent = new Intent();  // First time we've added a result.
@@ -460,6 +467,7 @@
      * @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will
      * be returned.
      */
+    @Source
     public static int getResultsSource(Intent intent) {
         Intent clipDataIntent = getClipDataIntentFromIntent(intent);
         if (clipDataIntent == null) {
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 7325daa7..4a6fa8c 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -16,6 +16,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.IBinder;
@@ -81,14 +82,6 @@
     }
 
     /**
-     * Temporary. Will be deleted.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
-        return addConfiguration(configKey, config);
-    }
-
-    /**
      * Clients can send a configuration and simultaneously registers the name of a broadcast
      * receiver that listens for when it should request data.
      *
@@ -233,10 +226,11 @@
      * the retrieved metrics from statsd memory.
      *
      * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
+     * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
+     * crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(long configKey) {
+    public @Nullable byte[] getData(long configKey) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
@@ -257,10 +251,10 @@
      * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
      * This getter is not destructive and will not reset any metrics/counters.
      *
-     * @return Serialized StatsdStatsReport proto. Returns null on failure.
+     * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getMetadata() {
+    public @Nullable byte[] getMetadata() {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6511f21..4cb7f89 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3769,7 +3769,7 @@
 
     /**
      * Called by an application that is administering the device to request that the storage system
-     * be encrypted.
+     * be encrypted. Does nothing if the caller is on a secondary user or a managed profile.
      * <p>
      * When multiple device administrators attempt to control device encryption, the most secure,
      * supported setting will always be used. If any device administrator requests device
@@ -3791,10 +3791,13 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param encrypt true to request encryption, false to release any previous request
-     * @return the new request status (for all active admins) - will be one of
-     *         {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, or
-     *         {@link #ENCRYPTION_STATUS_ACTIVE}. This is the value of the requests; Use
-     *         {@link #getStorageEncryptionStatus()} to query the actual device state.
+     * @return the new total request status (for all active admins), or {@link
+     *         DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} if called for a non-system user.
+     *         Will be one of {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link
+     *         #ENCRYPTION_STATUS_INACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE}. This is the value
+     *         of the requests; use {@link #getStorageEncryptionStatus()} to query the actual device
+     *         state.
+     *
      * @throws SecurityException if {@code admin} is not an active administrator or does not use
      *             {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
      */
@@ -8874,15 +8877,20 @@
      * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
      * specified, backups will be enabled.
      *
+     * <p>NOTE: The method shouldn't be called on the main thread.
+     *
      * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
+     * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public void setMandatoryBackupTransport(
-            @NonNull ComponentName admin, @Nullable ComponentName backupTransportComponent) {
+    @WorkerThread
+    public boolean setMandatoryBackupTransport(
+            @NonNull ComponentName admin,
+            @Nullable ComponentName backupTransportComponent) {
         throwIfParentInstance("setMandatoryBackupTransport");
         try {
-            mService.setMandatoryBackupTransport(admin, backupTransportComponent);
+            return mService.setMandatoryBackupTransport(admin, backupTransportComponent);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c29369f..c46402f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -364,7 +364,7 @@
 
     void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
     boolean isBackupServiceEnabled(in ComponentName admin);
-    void setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
+    boolean setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
     ComponentName getMandatoryBackupTransport();
 
     void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index c71bf2e..d67f11b 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -36,15 +36,16 @@
 public class JobParameters implements Parcelable {
 
     /** @hide */
-    public static final int REASON_CANCELED = 0;
+    public static final int REASON_CANCELED = JobProtoEnums.STOP_REASON_CANCELLED; // 0.
     /** @hide */
-    public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+    public static final int REASON_CONSTRAINTS_NOT_SATISFIED =
+            JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; //1.
     /** @hide */
-    public static final int REASON_PREEMPT = 2;
+    public static final int REASON_PREEMPT = JobProtoEnums.STOP_REASON_PREEMPT; // 2.
     /** @hide */
-    public static final int REASON_TIMEOUT = 3;
+    public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
     /** @hide */
-    public static final int REASON_DEVICE_IDLE = 4;
+    public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
 
     /** @hide */
     public static String getReasonName(int reason) {
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 0a61fab..8db38d3 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -39,7 +39,7 @@
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
         client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
-                "STOP_ACTIVITY_ITEM");
+                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 0d995e8..553c3ae 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -204,7 +204,8 @@
                     break;
                 case ON_STOP:
                     mTransactionHandler.handleStopActivity(r.token, false /* show */,
-                            0 /* configChanges */, mPendingActions, "LIFECYCLER_STOP_ACTIVITY");
+                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
+                            "LIFECYCLER_STOP_ACTIVITY");
                     break;
                 case ON_DESTROY:
                     mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 7e66fd7..01b13a2 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,7 +26,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
-import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
 import android.util.IntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -124,7 +124,7 @@
      *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
      */
     @VisibleForTesting
-    public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+    public int getClosestPreExecutionState(ActivityClientRecord r,
             int postExecutionState) {
         switch (postExecutionState) {
             case UNDEFINED:
@@ -147,7 +147,7 @@
      *         were provided or there is not path.
      */
     @VisibleForTesting
-    public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
         if (finalStates == null || finalStates.length == 0) {
             return UNDEFINED;
         }
@@ -168,6 +168,27 @@
         return closestState;
     }
 
+    /** Get the lifecycle state request to match the current state in the end of a transaction. */
+    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
+        final int prevState = r.getLifecycleState();
+        final ActivityLifecycleItem lifecycleItem;
+        switch (prevState) {
+            // TODO(lifecycler): Extend to support all possible states.
+            case ON_PAUSE:
+                lifecycleItem = PauseActivityItem.obtain();
+                break;
+            case ON_STOP:
+                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
+                        0 /* configChanges */);
+                break;
+            default:
+                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+                break;
+        }
+
+        return lifecycleItem;
+    }
+
     /**
      * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
      * that involves destruction and recreation if there is another path.
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index d6f2352..61679cb 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -66,10 +66,27 @@
             HINT_HORIZONTAL,
             HINT_PARTIAL,
             HINT_SEE_MORE,
-            HINT_KEY_WORDS
+            HINT_KEY_WORDS,
+            HINT_ERROR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceHint {}
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "SUBTYPE_" }, value = {
+            SUBTYPE_COLOR,
+            SUBTYPE_CONTENT_DESCRIPTION,
+            SUBTYPE_MAX,
+            SUBTYPE_MESSAGE,
+            SUBTYPE_PRIORITY,
+            SUBTYPE_RANGE,
+            SUBTYPE_SOURCE,
+            SUBTYPE_TOGGLE,
+            SUBTYPE_VALUE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceSubtype {}
 
     /**
      * Hint that this content is a title of other content in the slice. This can also indicate that
@@ -149,9 +166,14 @@
     /**
      * A hint to indicate that the contents of this subslice represent a list of keywords
      * related to the parent slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
     public static final String HINT_KEY_WORDS = "key_words";
     /**
+     * A hint to indicate that this slice represents an error.
+     */
+    public static final String HINT_ERROR = "error";
+    /**
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
@@ -168,14 +190,18 @@
     /**
      * Subtype to indicate that this is a message as part of a communication
      * sequence in this slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
     public static final String SUBTYPE_MESSAGE = "message";
     /**
      * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT},
+     * {@link SliceItem#FORMAT_IMAGE} or an {@link SliceItem#FORMAT_SLICE} containing them.
      */
     public static final String SUBTYPE_SOURCE = "source";
     /**
      * Subtype to tag an item as representing a color.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_COLOR = "color";
     /**
@@ -186,14 +212,18 @@
     public static final String SUBTYPE_SLIDER = "slider";
     /**
      * Subtype to tag an item as representing a range.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE} containing
+     * a {@link #SUBTYPE_VALUE} and possibly a {@link #SUBTYPE_MAX}.
      */
     public static final String SUBTYPE_RANGE = "range";
     /**
      * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_MAX = "max";
     /**
      * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_VALUE = "value";
     /**
@@ -205,10 +235,12 @@
     public static final String SUBTYPE_TOGGLE = "toggle";
     /**
      * Subtype to tag an item representing priority.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_PRIORITY = "priority";
     /**
      * Subtype to tag an item to use as a content description.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}.
      */
     public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
 
@@ -305,14 +337,24 @@
         private SliceSpec mSpec;
 
         /**
-         * Create a builder which will construct a {@link Slice} for the Given Uri.
-         * @param uri Uri to tag for this slice.
+         * @deprecated TO BE REMOVED
          */
+        @Deprecated
         public Builder(@NonNull Uri uri) {
             mUri = uri;
         }
 
         /**
+         * Create a builder which will construct a {@link Slice} for the given Uri.
+         * @param uri Uri to tag for this slice.
+         * @param spec the spec for this slice.
+         */
+        public Builder(@NonNull Uri uri, SliceSpec spec) {
+            mUri = uri;
+            mSpec = spec;
+        }
+
+        /**
          * Create a builder for a {@link Slice} that is a sub-slice of the slice
          * being constructed by the provided builder.
          * @param parent The builder constructing the parent slice
@@ -340,20 +382,13 @@
         /**
          * Add hints to the Slice being constructed
          */
-        public Builder addHints(@SliceHint String... hints) {
-            mHints.addAll(Arrays.asList(hints));
+        public Builder addHints(@SliceHint List<String> hints) {
+            mHints.addAll(hints);
             return this;
         }
 
         /**
-         * Add hints to the Slice being constructed
-         */
-        public Builder addHints(@SliceHint List<String> hints) {
-            return addHints(hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add the spec for this slice.
+         * @deprecated TO BE REMOVED
          */
         public Builder setSpec(SliceSpec spec) {
             mSpec = spec;
@@ -362,17 +397,10 @@
 
         /**
          * Add a sub-slice to the slice being constructed
-         */
-        public Builder addSubSlice(@NonNull Slice slice) {
-            return addSubSlice(slice, null);
-        }
-
-        /**
-         * Add a sub-slice to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addSubSlice(@NonNull Slice slice, @Nullable String subType) {
+        public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) {
             mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
                     slice.getHints().toArray(new String[slice.getHints().size()])));
             return this;
@@ -380,18 +408,11 @@
 
         /**
          * Add an action to the slice being constructed
-         */
-        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
-            return addAction(action, s, null);
-        }
-
-        /**
-         * Add an action to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
         public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
-                @Nullable String subType) {
+                @Nullable @SliceSubtype String subType) {
             List<String> hints = s.getHints();
             s.mSpec = null;
             mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
@@ -404,58 +425,31 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Builder addText(CharSequence text, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(text, SliceItem.FORMAT_TEXT, subType, hints));
             return this;
         }
 
         /**
-         * Add text to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addText(text, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add an image to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
             return this;
         }
 
         /**
-         * Add an image to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint List<String> hints) {
-            return addIcon(icon, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add remote input to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput,
+                @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add remote input to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
-                @SliceHint String... hints) {
             mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
                     subType, hints));
             return this;
@@ -466,70 +460,48 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addInt(int value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints));
             return this;
         }
 
         /**
-         * Add an integer to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
+         * @deprecated TO BE REMOVED.
          */
-        public Builder addInt(int value, @Nullable String subType,
+        @Deprecated
+        public Slice.Builder addTimestamp(long time, @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addInt(value, subType, hints.toArray(new String[hints.size()]));
+            return addLong(time, subType, hints);
         }
 
         /**
-         * Add a timestamp to the slice being constructed
+         * Add a long to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint String... hints) {
-            mItems.add(new SliceItem(time, SliceItem.FORMAT_TIMESTAMP, subType,
-                    hints));
+        public Slice.Builder addLong(long value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
+            mItems.add(new SliceItem(value, SliceItem.FORMAT_LONG, subType,
+                    hints.toArray(new String[hints.size()])));
             return this;
         }
 
         /**
-         * Add a timestamp to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add a bundle to the slice being constructed.
          * <p>Expected to be used for support library extension, should not be used for general
          * development
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
                     hints));
             return this;
         }
 
         /**
-         * Add a bundle to the slice being constructed.
-         * <p>Expected to be used for support library extension, should not be used for general
-         * development
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addBundle(bundle, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Construct the slice.
          */
         public Slice build() {
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 9eb2bb8..019ae49 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -67,7 +67,7 @@
             FORMAT_IMAGE,
             FORMAT_ACTION,
             FORMAT_INT,
-            FORMAT_TIMESTAMP,
+            FORMAT_LONG,
             FORMAT_REMOTE_INPUT,
             FORMAT_BUNDLE,
     })
@@ -98,9 +98,14 @@
      */
     public static final String FORMAT_INT = "int";
     /**
-     * A {@link SliceItem} that contains a timestamp.
+     * A {@link SliceItem} that contains a long.
      */
-    public static final String FORMAT_TIMESTAMP = "timestamp";
+    public static final String FORMAT_LONG = "long";
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public static final String FORMAT_TIMESTAMP = FORMAT_LONG;
     /**
      * A {@link SliceItem} that contains a {@link RemoteInput}.
      */
@@ -123,6 +128,14 @@
      * @hide
      */
     public SliceItem(Object obj, @SliceType String format, String subType,
+            List<String> hints) {
+        this(obj, format, subType, hints.toArray(new String[hints.size()]));
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem(Object obj, @SliceType String format, String subType,
             @Slice.SliceHint String[] hints) {
         mHints = hints;
         mFormat = format;
diff --git a/core/java/android/app/slice/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
new file mode 100644
index 0000000..a7069bc
--- /dev/null
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * Metrics interface for slices.
+ *
+ * This is called by SliceView, so Slice develoers should
+ * not need to reference this class.
+ *
+ * @see androidx.slice.widget.SliceView
+ */
+public class SliceMetrics {
+
+    private static final String TAG = "SliceMetrics";
+    private MetricsLogger mMetricsLogger;
+
+    /**
+     * An object to be used throughout the life of a slice to register events.
+     */
+    public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    /**
+     * To be called whenever the slice becomes visible to the user.
+     */
+    public void logVisible() {
+    }
+
+    /**
+     * To be called whenever the slice becomes invisible to the user.
+     */
+    public void logHidden() {
+    }
+
+    /**
+     * To be called whenever the use interacts with a slice.
+     *@param subSlice The URI of the sub-slice that is the subject of the interaction.
+     */
+    public void logTouch(@NonNull Uri subSlice) {
+    }
+}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa2cf46..dd89293 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -41,6 +41,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -430,9 +431,11 @@
         return new Slice.Builder(sliceUri)
                 .addAction(createPermissionIntent(context, sliceUri, callingPackage),
                         new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null)
-                                .build())
-                .addHints(Slice.HINT_LIST_ITEM)
+                                .addText(getPermissionString(context, callingPackage), null,
+                                        Collections.emptyList())
+                                .build(),
+                        null)
+                .addHints(Arrays.asList(Slice.HINT_LIST_ITEM))
                 .build();
     }
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 4c7e97b..b354e81 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -81,6 +81,7 @@
          * An event type denoting that a package was interacted with in some way by the system.
          * @hide
          */
+        @SystemApi
         public static final int SYSTEM_INTERACTION = 6;
 
         /**
@@ -124,6 +125,20 @@
         @SystemApi
         public static final int NOTIFICATION_INTERRUPTION = 12;
 
+        /**
+         * A Slice was pinned by the default launcher or the default assistant.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED_PRIV = 13;
+
+        /**
+         * A Slice was pinned by an app.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED = 14;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 59f001c..1c384580 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -107,25 +107,35 @@
     public static final int STANDBY_BUCKET_EXEMPTED = 5;
 
     /**
-     * The app was used very recently, currently in use or likely to be used very soon.
+     * The app was used very recently, currently in use or likely to be used very soon. Standby
+     * bucket values that are &le; {@link #STANDBY_BUCKET_ACTIVE} will not be throttled by the
+     * system while they are in this bucket. Buckets &gt; {@link #STANDBY_BUCKET_ACTIVE} will most
+     * likely be restricted in some way. For instance, jobs and alarms may be deferred.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_ACTIVE = 10;
 
     /**
-     * The app was used recently and/or likely to be used in the next few hours.
+     * The app was used recently and/or likely to be used in the next few hours. Restrictions will
+     * apply to these apps, such as deferral of jobs and alarms.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_WORKING_SET = 20;
 
     /**
      * The app was used in the last few days and/or likely to be used in the next few days.
+     * Restrictions will apply to these apps, such as deferral of jobs and alarms. The delays may be
+     * greater than for apps in higher buckets (lower bucket value). Bucket values &gt;
+     * {@link #STANDBY_BUCKET_FREQUENT} may additionally have network access limited.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_FREQUENT = 30;
 
     /**
      * The app has not be used for several days and/or is unlikely to be used for several days.
+     * Apps in this bucket will have the most restrictions, including network restrictions, except
+     * during certain short periods (at a minimum, once a day) when they are allowed to execute
+     * jobs, access the network, etc.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_RARE = 40;
@@ -393,11 +403,19 @@
     /**
      * Returns the current standby bucket of the calling app. The system determines the standby
      * state of the app based on app usage patterns. Standby buckets determine how much an app will
-     * be restricted from running background tasks such as jobs, alarms and certain PendingIntent
-     * callbacks.
+     * be restricted from running background tasks such as jobs and alarms.
      * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
      * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
      * restrictive. The battery level of the device might also affect the restrictions.
+     * <p>Apps in buckets &le; {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
+     * Apps in buckets &gt; {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
+     * running in the background.
+     * <p>The standby state of an app can change at any time either due to a user interaction or a
+     * system interaction or some algorithm determining that the app can be restricted for a period
+     * of time before the user has a need for it.
+     * <p>You can also query the recent history of standby bucket changes by calling
+     * {@link #queryEventsForSelf(long, long)} and searching for
+     * {@link UsageEvents.Event#STANDBY_BUCKET_CHANGED}.
      *
      * @return the current standby bucket of the calling app. One of STANDBY_BUCKET_* constants.
      */
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index e736f34..20248b9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -35,7 +35,6 @@
 import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
@@ -680,11 +679,13 @@
     }
 
     /**
-     * Updates the info for the supplied AppWidget provider.
+     * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
+     * behavior of the widget based on the state of the app (for e.g., if the user is logged in
+     * or not). Calling this API completely replaces the previous definition.
      *
      * <p>
      * The manifest entry of the provider should contain an additional meta-data tag similar to
-     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any additional definitions for
+     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for
      * the provider.
      *
      * <p>
@@ -1186,6 +1187,11 @@
      * calls this API multiple times in a row.  It may ignore the previous requests,
      * for example.
      *
+     * <p>Launcher will not show the configuration activity associated with the provider in this
+     * case. The app could either show the configuration activity as a response to the callback,
+     * or show if before calling the API (various configurations can be encapsulated in
+     * {@code successCallback} to avoid persisting them before the widgetId is known).
+     *
      * @param provider The {@link ComponentName} for the {@link
      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
      * @param extras In not null, this is passed to the launcher app. For eg {@link
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ee667c2..b9e80e4 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -680,6 +680,10 @@
         if (!getLeAccess()) {
             return null;
         }
+        if (!isMultipleAdvertisementSupported()) {
+            Log.e(TAG, "Bluetooth LE advertising not supported");
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeAdvertiser == null) {
                 sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 647e0d0..8f8083e 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -379,6 +380,76 @@
     }
 
     /**
+     * Select a connected device as active.
+     *
+     * The active device selection is per profile. An active device's
+     * purpose is profile-specific. For example, Hearing Aid audio
+     * streaming is to the active Hearing Aid device. If a remote device
+     * is not connected, it cannot be selected as active.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is not connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that the
+     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+     * with the active device.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device the remote Bluetooth device. Could be null to clear
+     * the active device and stop streaming audio to a Bluetooth device.
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+        if (DBG) log("setActiveDevice(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                mService.setActiveDevice(device);
+                return true;
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Check whether the device is active.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * permission.
+     *
+     * @return the connected device that is active or null if no device
+     * is active
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isActiveDevice(@Nullable BluetoothDevice device) {
+        if (VDBG) log("isActiveDevice()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                return mService.isActiveDevice(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
      * Set priority of the profile
      *
      * <p> The device should already be paired.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index ce7d3af..440103a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2464,8 +2464,9 @@
      * @param account the account to specify in the sync
      * @param authority the provider to specify in the sync request
      * @param extras extra parameters to go along with the sync request
-     * @param pollFrequency how frequently the sync should be performed, in seconds. A minimum value
-     *                      of 1 hour is enforced.
+     * @param pollFrequency how frequently the sync should be performed, in seconds.
+     * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+     * On previous versions, the minimum interval is 1 hour.
      * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
      * are null.
      */
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 36a74a4..f4352f9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,8 +50,8 @@
 import android.content.pm.dex.IArtManager;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.content.IntentSender;
 
 /**
@@ -272,9 +272,17 @@
 
     void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
 
-    String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, int userId);
+    String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
+            in PersistableBundle launcherExtras, in PersistableBundle appExtras,
+            String callingPackage, int userId);
+
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
+    PersistableBundle getPackageSuspendedAppExtras(String pacakgeName, int userId);
+
+    void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
+            int userId);
+
     /**
      * Backup/restore support - only the system uid may use these.
      */
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index b4a7eec..21ede16 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1429,6 +1429,10 @@
          * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
          * different request type.
          *
+         * <p>Launcher should not show any configuration activity associated with the provider, and
+         * assume that the widget is already fully configured. Upon accepting the widget, it should
+         * pass the widgetId in {@link #accept(Bundle)}.
+         *
          * @return requested {@link AppWidgetProviderInfo} when a request is of the
          * {@link #REQUEST_TYPE_APPWIDGET} type.  Null otherwise.
          */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bd7961f..4d8773c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -51,6 +51,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -4183,6 +4184,12 @@
     public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
 
     /**
+     * @hide
+     */
+    public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId);
+
+    /**
      * Retrieve all services that can match the given intent.
      *
      * @param intent The desired intent as per resolveService().
@@ -5504,28 +5511,49 @@
     /**
      * Puts the package in a suspended state, where attempts at starting activities are denied.
      *
-     * <p>It doesn't remove the data or the actual package file. The application notifications
-     * will be hidden, the application will not show up in recents, will not be able to show
-     * toasts or dialogs or ring the device.
+     * <p>It doesn't remove the data or the actual package file. The application's notifications
+     * will be hidden, any of the it's started activities will be stopped and it will not be able to
+     * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
+     * system dialog with the given {@code dialogMessage} will be shown instead.</p>
      *
      * <p>The package must already be installed. If the package is uninstalled while suspended
-     * the package will no longer be suspended.
+     * the package will no longer be suspended. </p>
+     *
+     * <p>Optionally, the suspending app can provide extra information in the form of
+     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+     * launcher to support customization that they might need to handle the suspended state. </p>
+     *
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
+     * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
      *
      * @param packageNames The names of the packages to set the suspended status.
      * @param suspended If set to {@code true} than the packages will be suspended, if set to
-     * {@code false} the packages will be unsuspended.
-     * @param userId The user id.
+     * {@code false}, the packages will be unsuspended.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogMessage The message to be displayed to the user, when they try to launch a
+     *                      suspended app.
      *
      * @return an array of package names for which the suspended status is not set as requested in
      * this method.
      *
      * @hide
      */
-    public abstract String[] setPackagesSuspendedAsUser(
-            String[] packageNames, boolean suspended, @UserIdInt int userId);
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.SUSPEND_APPS,
+            Manifest.permission.MANAGE_USERS})
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            String dialogMessage) {
+        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+    }
 
     /**
-     * @see #setPackageSuspendedAsUser(String, boolean, int)
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
      * @param packageName The name of the package to get the suspended status of.
      * @param userId The user id.
      * @return {@code true} if the package is suspended or {@code false} if the package is not
@@ -5535,6 +5563,86 @@
     public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
 
     /**
+     * Query if an app is currently suspended.
+     *
+     * @return {@code true} if the given package is suspended, {@code false} otherwise
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     * @hide
+     */
+    @SystemApi
+    public boolean isPackageSuspended(String packageName) {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Apps can query this to know if they have been suspended.
+     *
+     * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+     *
+     * @see #getSuspendedPackageAppExtras()
+     */
+    public boolean isPackageSuspended() {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
+     * package was suspended.
+     *
+     * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
+     * api.</p>
+     *
+     * @param packageName The package to retrieve extras for.
+     * @return The {@code appExtras} for the suspended package.
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
+     * Set the app extras for a suspended package. This method can be used to update the appExtras
+     * for a package that was earlier suspended using
+     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * String)}
+     * Does nothing if the given package is not already in a suspended state.
+     *
+     * @param packageName The package for which the appExtras need to be updated
+     * @param appExtras The new appExtras for the given package
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    public void setSuspendedPackageAppExtras(String packageName,
+            @Nullable PersistableBundle appExtras) {
+        throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
+     * Returns any extra information supplied as {@code appExtras} to the system when the calling
+     * app was suspended.
+     *
+     * <p> Note: This just returns whatever {@link PersistableBundle} was passed to the system via
+     * {@code setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * String)} when suspending the package, <em> which might be {@code null}. </em></p>
+     *
+     * @return A {@link PersistableBundle} containing the extras for the app, or {@code null} if the
+     * package is not currently suspended.
+     * @see #isPackageSuspended()
+     */
+    public @Nullable PersistableBundle getSuspendedPackageAppExtras() {
+        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
      * Provide a hint of what the {@link ApplicationInfo#category} value should
      * be for the given package.
      * <p>
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 293beb2..f7b6e09 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -27,6 +27,8 @@
 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
 import android.util.ArraySet;
 
 import com.android.internal.util.ArrayUtils;
@@ -44,6 +46,9 @@
     public boolean notLaunched;
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
+    public String suspendingPackage;
+    public PersistableBundle suspendedAppExtras;
+    public PersistableBundle suspendedLauncherExtras;
     public boolean instantApp;
     public boolean virtualPreload;
     public int enabled;
@@ -76,6 +81,9 @@
         notLaunched = o.notLaunched;
         hidden = o.hidden;
         suspended = o.suspended;
+        suspendingPackage = o.suspendingPackage;
+        suspendedAppExtras = o.suspendedAppExtras;
+        suspendedLauncherExtras = o.suspendedLauncherExtras;
         instantApp = o.instantApp;
         virtualPreload = o.virtualPreload;
         enabled = o.enabled;
@@ -195,6 +203,20 @@
         if (suspended != oldState.suspended) {
             return false;
         }
+        if (suspended) {
+            if (suspendingPackage == null
+                    || !suspendingPackage.equals(oldState.suspendingPackage)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(suspendedAppExtras,
+                    oldState.suspendedAppExtras)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
+                    oldState.suspendedLauncherExtras)) {
+                return false;
+            }
+        }
         if (instantApp != oldState.instantApp) {
             return false;
         }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 2a791ec..316c796 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -293,9 +293,7 @@
                     (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
             // Use compatibility WAL unless an app explicitly set journal/synchronous mode
             // or DISABLE_COMPATIBILITY_WAL flag is set
-            final boolean useCompatibilityWal = mConfiguration.journalMode == null
-                    && mConfiguration.syncMode == null
-                    && (mConfiguration.openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0;
+            final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal();
             if (walEnabled || useCompatibilityWal) {
                 setJournalMode("WAL");
                 if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index dadb95b..e519302 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -23,6 +23,7 @@
 import android.os.Message;
 import android.os.OperationCanceledException;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrefixPrinter;
 import android.util.Printer;
@@ -1111,6 +1112,11 @@
             printer.println("  Open: " + mIsOpen);
             printer.println("  Max connections: " + mMaxConnectionPoolSize);
             printer.println("  Total execution time: " + mTotalExecutionTimeCounter);
+            printer.println("  Configuration: openFlags=" + mConfiguration.openFlags
+                    + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal()
+                    + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode)
+                    + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode));
+
             if (SQLiteCompatibilityWalFlags.areFlagsSet()) {
                 printer.println("  Compatibility WAL settings: compatibility_wal_supported="
                         + SQLiteCompatibilityWalFlags
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 275043f..8b9dfcf 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -194,6 +194,11 @@
         return path.equalsIgnoreCase(MEMORY_DB_PATH);
     }
 
+    boolean useCompatibilityWal() {
+        return journalMode == null && syncMode == null
+                && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0;
+    }
+
     private static String stripPathForLogs(String path) {
         if (path.indexOf('@') == -1) {
             return path;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 8502fc4..390b83f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -28,7 +28,9 @@
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * <p>The properties describing a
@@ -450,23 +452,20 @@
     }
 
     /**
-     * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+     * Returns the set 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>
+     * doesn't have the capability, the return value will be an empty set. </p>
      *
-     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * <p>The set 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.
+     * @return Set of physical camera ids for this logical camera device.
      */
     @NonNull
-    public List<String> getPhysicalCameraIds() {
+    public Set<String> getPhysicalCameraIds() {
         int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
         if (availableCapabilities == null) {
             throw new AssertionError("android.request.availableCapabilities must be non-null "
@@ -475,7 +474,7 @@
 
         if (!ArrayUtils.contains(availableCapabilities,
                 REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
-            return Collections.emptyList();
+            return Collections.emptySet();
         }
         byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
 
@@ -485,9 +484,9 @@
         } catch (java.io.UnsupportedEncodingException e) {
             throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
         }
-        String[] physicalCameraIdList = physicalCamIdString.split("\0");
+        String[] physicalCameraIdArray = physicalCamIdString.split("\0");
 
-        return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+        return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -1243,7 +1242,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -1257,10 +1256,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -1306,7 +1305,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -1319,9 +1318,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -1363,7 +1362,14 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
@@ -1388,6 +1394,46 @@
             new Key<Integer>("android.lens.poseReference", int.class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1419,6 +1465,8 @@
      * consideration of future support.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer; replaced by better partials mechanism</p>
+
      * @hide
      */
     @Deprecated
@@ -1809,6 +1857,8 @@
      * <p>When set to YUV_420_888, application can access the YUV420 data directly.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1829,6 +1879,8 @@
      * TODO: Remove property.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1845,6 +1897,8 @@
      *
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1884,6 +1938,8 @@
      * <p><b>Units</b>: Nanoseconds</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1906,6 +1962,8 @@
      * check if it limits the maximum size for image data.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2545,7 +2603,7 @@
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
      * <p>The currently supported fields that correct for geometric distortion are:</p>
      * <ol>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
      * </ol>
      * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
      * as the post-distortion-corrected rectangle given in
@@ -2558,7 +2616,7 @@
      * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
      * <p>This key is available on all devices.</p>
      *
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 72db33f..f47d464 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -819,7 +819,8 @@
      * @param config A session configuration (see {@link SessionConfiguration}).
      *
      * @throws IllegalArgumentException In case the session configuration is invalid; or the output
-     *                                  configurations are empty.
+     *                                  configurations are empty; or the session configuration
+     *                                  executor is invalid.
      * @throws CameraAccessException In case the camera device is no longer connected or has
      *                               encountered a fatal error.
      * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a2bc91e..4d64295 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -25,6 +25,7 @@
 import android.hardware.CameraStatus;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -41,6 +42,11 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 /**
  * <p>A system service manager for detecting, characterizing, and connecting to
@@ -123,16 +129,8 @@
      */
     public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
             @Nullable Handler handler) {
-        if (handler == null) {
-            Looper looper = Looper.myLooper();
-            if (looper == null) {
-                throw new IllegalArgumentException(
-                        "No handler given, and current thread has no looper!");
-            }
-            handler = new Handler(looper);
-        }
-
-        CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
+        CameraManagerGlobal.get().registerAvailabilityCallback(callback,
+                CameraDeviceImpl.checkAndWrapHandler(handler));
     }
 
     /**
@@ -170,15 +168,8 @@
      *             no looper.
      */
     public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
-        if (handler == null) {
-            Looper looper = Looper.myLooper();
-            if (looper == null) {
-                throw new IllegalArgumentException(
-                        "No handler given, and current thread has no looper!");
-            }
-            handler = new Handler(looper);
-        }
-        CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+        CameraManagerGlobal.get().registerTorchCallback(callback,
+                CameraDeviceImpl.checkAndWrapHandler(handler));
     }
 
     /**
@@ -728,12 +719,13 @@
          */
         private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
 
+        private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
         // Camera ID -> Status map
         private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
 
-        // Registered availablility callbacks and their handlers
-        private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
-            new ArrayMap<AvailabilityCallback, Handler>();
+        // Registered availablility callbacks and their executors
+        private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
+            new ArrayMap<AvailabilityCallback, Executor>();
 
         // torch client binder to set the torch mode with.
         private Binder mTorchClientBinder = new Binder();
@@ -741,9 +733,9 @@
         // Camera ID -> Torch status map
         private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
 
-        // Registered torch callbacks and their handlers
-        private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
-                new ArrayMap<TorchCallback, Handler>();
+        // Registered torch callbacks and their executors
+        private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
+                new ArrayMap<TorchCallback, Executor>();
 
         private final Object mLock = new Object();
 
@@ -925,49 +917,63 @@
             }
         }
 
-        private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
+        private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
                 final String id, final int status) {
             if (isAvailable(status)) {
-                handler.post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            callback.onCameraAvailable(id);
-                        }
-                    });
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onCameraAvailable(id);
+                            }
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             } else {
-                handler.post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            callback.onCameraUnavailable(id);
-                        }
-                    });
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onCameraUnavailable(id);
+                            }
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
 
-        private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+        private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
                 final String id, final int status) {
             switch(status) {
                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
-                case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
-                    handler.post(
-                            new Runnable() {
-                                @Override
-                                public void run() {
-                                    callback.onTorchModeChanged(id, status ==
-                                            ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
-                                }
+                case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> {
+                                callback.onTorchModeChanged(id, status ==
+                                        ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
                             });
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    }
                     break;
-                default:
-                    handler.post(
-                            new Runnable() {
-                                @Override
-                                public void run() {
-                                    callback.onTorchModeUnavailable(id);
-                                }
+                default: {
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> {
+                                callback.onTorchModeUnavailable(id);
                             });
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    }
                     break;
             }
         }
@@ -976,11 +982,11 @@
          * Send the state of all known cameras to the provided listener, to initialize
          * the listener's knowledge of camera state.
          */
-        private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+        private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
             for (int i = 0; i < mDeviceStatus.size(); i++) {
                 String id = mDeviceStatus.keyAt(i);
                 Integer status = mDeviceStatus.valueAt(i);
-                postSingleUpdate(callback, handler, id, status);
+                postSingleUpdate(callback, executor, id, status);
             }
         }
 
@@ -1039,18 +1045,18 @@
 
             final int callbackCount = mCallbackMap.size();
             for (int i = 0; i < callbackCount; i++) {
-                Handler handler = mCallbackMap.valueAt(i);
+                Executor executor = mCallbackMap.valueAt(i);
                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
 
-                postSingleUpdate(callback, handler, id, status);
+                postSingleUpdate(callback, executor, id, status);
             }
         } // onStatusChangedLocked
 
-        private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+        private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
             for (int i = 0; i < mTorchStatus.size(); i++) {
                 String id = mTorchStatus.keyAt(i);
                 Integer status = mTorchStatus.valueAt(i);
-                postSingleTorchUpdate(callback, handler, id, status);
+                postSingleTorchUpdate(callback, executor, id, status);
             }
         }
 
@@ -1078,9 +1084,9 @@
 
             final int callbackCount = mTorchCallbackMap.size();
             for (int i = 0; i < callbackCount; i++) {
-                final Handler handler = mTorchCallbackMap.valueAt(i);
+                final Executor executor = mTorchCallbackMap.valueAt(i);
                 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
-                postSingleTorchUpdate(callback, handler, id, status);
+                postSingleTorchUpdate(callback, executor, id, status);
             }
         } // onTorchStatusChangedLocked
 
@@ -1089,16 +1095,16 @@
          * global listener singleton.
          *
          * @param callback the new callback to send camera availability notices to
-         * @param handler The handler on which the callback should be invoked. May not be null.
+         * @param executor The executor which should invoke the callback. May not be null.
          */
-        public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+        public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
             synchronized (mLock) {
                 connectCameraServiceLocked();
 
-                Handler oldHandler = mCallbackMap.put(callback, handler);
+                Executor oldExecutor = mCallbackMap.put(callback, executor);
                 // For new callbacks, provide initial availability information
-                if (oldHandler == null) {
-                    updateCallbackLocked(callback, handler);
+                if (oldExecutor == null) {
+                    updateCallbackLocked(callback, executor);
                 }
 
                 // If not connected to camera service, schedule a reconnect to camera service.
@@ -1120,14 +1126,14 @@
             }
         }
 
-        public void registerTorchCallback(TorchCallback callback, Handler handler) {
+        public void registerTorchCallback(TorchCallback callback, Executor executor) {
             synchronized(mLock) {
                 connectCameraServiceLocked();
 
-                Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+                Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
                 // For new callbacks, provide initial torch information
-                if (oldHandler == null) {
-                    updateTorchCallbackLocked(callback, handler);
+                if (oldExecutor == null) {
+                    updateTorchCallbackLocked(callback, executor);
                 }
 
                 // If not connected to camera service, schedule a reconnect to camera service.
@@ -1165,13 +1171,7 @@
          * availability callback or torch status callback.
          */
         private void scheduleCameraServiceReconnectionLocked() {
-            final Handler handler;
-
-            if (mCallbackMap.size() > 0) {
-                handler = mCallbackMap.valueAt(0);
-            } else if (mTorchCallbackMap.size() > 0) {
-                handler = mTorchCallbackMap.valueAt(0);
-            } else {
+            if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
                 // Not necessary to reconnect camera service if no client registers a callback.
                 return;
             }
@@ -1181,22 +1181,21 @@
                         " ms");
             }
 
-            handler.postDelayed(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            ICameraService cameraService = getCameraService();
-                            if (cameraService == null) {
-                                synchronized(mLock) {
-                                    if (DEBUG) {
-                                        Log.v(TAG, "Reconnecting Camera Service failed.");
-                                    }
-                                    scheduleCameraServiceReconnectionLocked();
-                                }
+            try {
+                mScheduler.schedule(() -> {
+                    ICameraService cameraService = getCameraService();
+                    if (cameraService == null) {
+                        synchronized(mLock) {
+                            if (DEBUG) {
+                                Log.v(TAG, "Reconnecting Camera Service failed.");
                             }
+                            scheduleCameraServiceReconnectionLocked();
                         }
-                    },
-                    CAMERA_SERVICE_RECONNECT_DELAY_MS);
+                    }
+                }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
+            }
         }
 
         /**
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 14c2865..7669c01 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -684,7 +684,7 @@
      * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
      * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
      * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
      * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
@@ -702,12 +702,12 @@
      * rate, including depth stall time.</p>
      *
      * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_FACING
      * @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#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
@@ -826,7 +826,7 @@
      * <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>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
      * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
@@ -852,11 +852,11 @@
      * 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_DISTORTION
      * @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
      */
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 98901a1..b0cbec7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -808,7 +808,7 @@
          *
          *<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
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty set 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>
@@ -2791,8 +2791,10 @@
      *   <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>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e84e48f..6331942 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2783,7 +2783,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -2797,10 +2797,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -2846,7 +2846,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -2859,9 +2859,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -2903,12 +2903,59 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
      * excessive noise added by the capture process, especially in dark conditions.</p>
@@ -2981,6 +3028,8 @@
      * Optional. Default value is FINAL.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2997,6 +3046,8 @@
      * &gt; 0</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -3777,6 +3828,8 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3801,6 +3854,8 @@
      * regardless of the android.control.* current values.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3919,8 +3974,10 @@
      *   <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>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 0be45a0..4e20cb8 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -138,7 +138,7 @@
      *
      * <p>This function can be called for logical multi-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
+     * CameraCharacteristics#getPhysicalCameraIds} return a non-empty set of physical devices that
      * are backing the logical camera.</p>
      *
      * <p>If one or more streams from the underlying physical cameras were requested by the
diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
deleted file mode 100644
index 866f370..0000000
--- a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * A dispatcher that replaces one argument with another; replaces any argument at an index
- * with another argument.
- *
- * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
- * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
- * be something
- * that's not an {@code int}.</p>
- *
- * @param <T>
- *          source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <TArg>
- *          argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
- *          will be overriden to objects of this type
- */
-public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
-
-    private final Dispatchable<T> mTarget;
-    private final int mArgumentIndex;
-    private final TArg mReplaceWith;
-
-    /**
-     * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
-     * after the argument is replaced.
-     *
-     * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
-     * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
-     * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
-     *
-     * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
-     * passed through with the arguments unchanged.</p>
-     *
-     * @param target destination dispatch type, methods will be redirected to this dispatcher
-     * @param argumentIndex the numeric index of the argument {@code >= 0}
-     * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
-     */
-    public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
-            TArg replaceWith) {
-        mTarget = checkNotNull(target, "target must not be null");
-        mArgumentIndex = checkArgumentNonnegative(argumentIndex,
-                "argumentIndex must not be negative");
-        mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) throws Throwable {
-
-        if (args.length > mArgumentIndex) {
-            args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
-            args[mArgumentIndex] = mReplaceWith;
-        }
-
-        return mTarget.dispatch(method, args);
-    }
-
-    private static Object[] arrayCopy(Object[] array) {
-        int length = array.length;
-        Object[] newArray = new Object[length];
-        for (int i = 0; i < length; ++i) {
-            newArray[i] = array[i];
-        }
-        return newArray;
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
deleted file mode 100644
index fe575b2..0000000
--- a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Broadcast a single dispatch into multiple other dispatchables.
- *
- * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
- * see the same dispatch as well. The first target's return value is returned.</p>
- *
- * <p>This enables a single listener to be converted into a multi-listener.</p>
- */
-public class BroadcastDispatcher<T> implements Dispatchable<T> {
-
-    private final List<Dispatchable<T>> mDispatchTargets;
-
-    /**
-     * Create a broadcast dispatcher from the supplied dispatch targets.
-     *
-     * @param dispatchTargets one or more targets to dispatch to
-     */
-    @SafeVarargs
-    public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
-        mDispatchTargets = Arrays.asList(
-                checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) throws Throwable {
-        Object result = null;
-        boolean gotResult = false;
-
-        for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
-            Object localResult = dispatchTarget.dispatch(method, args);
-
-            if (!gotResult) {
-                gotResult = true;
-                result = localResult;
-            }
-        }
-
-        return result;
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
deleted file mode 100644
index 753103f..0000000
--- a/core/java/android/hardware/camera2/dispatch/Dispatchable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-/**
- * Dynamically dispatch a method and its argument to some object.
- *
- * <p>This can be used to intercept method calls and do work around them, redirect work,
- * or block calls entirely.</p>
- */
-public interface Dispatchable<T> {
-    /**
-     * Dispatch the method and arguments to this object.
-     * @param method a method defined in class {@code T}
-     * @param args arguments corresponding to said {@code method}
-     * @return the object returned when invoking {@code method}
-     * @throws Throwable any exception that might have been raised while invoking the method
-     */
-    public Object dispatch(Method method, Object[] args) throws Throwable;
-}
diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
deleted file mode 100644
index 75f97e4..0000000
--- a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Duck typing dispatcher; converts dispatch methods calls from one class to another by
- * looking up equivalently methods at runtime by name.
- *
- * <p>For example, if two types have identical method names and arguments, but
- * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
- * made from one type to the other.</p>
- *
- * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
- */
-public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
-
-    private final MethodNameInvoker<T> mDuck;
-
-    /**
-     * Create a new duck typing dispatcher.
-     *
-     * @param target destination dispatch type, methods will be redirected to this dispatcher
-     * @param targetClass destination dispatch class, methods will be converted to this class's
-     */
-    public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
-        checkNotNull(targetClass, "targetClass must not be null");
-        checkNotNull(target, "target must not be null");
-
-        mDuck = new MethodNameInvoker<T>(target, targetClass);
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        return mDuck.invoke(method.getName(), args);
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
deleted file mode 100644
index f8e9d49..0000000
--- a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.os.Handler;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Forward all interface calls into a handler by posting it as a {@code Runnable}.
- *
- * <p>All calls will return immediately; functions with return values will return a default
- * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
- *
- * <p>Any exceptions thrown on the handler while trying to invoke a method
- * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
- * checked exceptions to be thrown will result in "undefined" behavior
- * (although in practice it is usually thrown as normal).</p>
- */
-public class HandlerDispatcher<T> implements Dispatchable<T> {
-
-    private static final String TAG = "HandlerDispatcher";
-
-    private final Dispatchable<T> mDispatchTarget;
-    private final Handler mHandler;
-
-    /**
-     * Create a dispatcher that forwards it's dispatch calls by posting
-     * them onto the {@code handler} as a {@code Runnable}.
-     *
-     * @param dispatchTarget the destination whose method calls will be redirected into the handler
-     * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
-     * @param <T> the type of the element you want to wrap.
-     * @return a dispatcher that will forward it's dispatch calls to a handler
-     */
-    public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
-        mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-        mHandler = checkNotNull(handler, "handler must not be null");
-    }
-
-    @Override
-    public Object dispatch(final Method method, final Object[] args) throws Throwable {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mDispatchTarget.dispatch(method, args);
-                } catch (InvocationTargetException e) {
-                    Throwable t = e.getTargetException();
-                    // Potential UB. Hopefully 't' is a runtime exception.
-                    UncheckedThrow.throwAnyException(t);
-                } catch (IllegalAccessException e) {
-                    // Impossible
-                    Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
-                } catch (IllegalArgumentException e) {
-                    // Impossible
-                    Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
-                } catch (Throwable e) {
-                    UncheckedThrow.throwAnyException(e);
-                }
-            }
-        });
-
-        // TODO handle primitive return values that would avoid NPE if unboxed
-        return null;
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
deleted file mode 100644
index ac5f526..0000000
--- a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-
-public class InvokeDispatcher<T> implements Dispatchable<T> {
-
-    private static final String TAG = "InvocationSink";
-    private final T mTarget;
-
-    public InvokeDispatcher(T target) {
-        mTarget = checkNotNull(target, "target must not be null");
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        try {
-            return method.invoke(mTarget, args);
-        } catch (InvocationTargetException e) {
-            Throwable t = e.getTargetException();
-            // Potential UB. Hopefully 't' is a runtime exception.
-            UncheckedThrow.throwAnyException(t);
-        } catch (IllegalAccessException e) {
-            // Impossible
-            Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
-        } catch (IllegalArgumentException e) {
-            // Impossible
-            Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
-        }
-
-        // unreachable
-        return null;
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
deleted file mode 100644
index 8296b7a..0000000
--- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
- *
- * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
- */
-public class MethodNameInvoker<T> {
-
-    private final Dispatchable<T> mTarget;
-    private final Class<T> mTargetClass;
-    private final Method[] mTargetClassMethods;
-    private final ConcurrentHashMap<String, Method> mMethods =
-            new ConcurrentHashMap<>();
-
-    /**
-     * Create a new method name invoker.
-     *
-     * @param target destination dispatch type, invokes will be redirected to this dispatcher
-     * @param targetClass destination dispatch class, the invoked methods will be from this class
-     */
-    public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
-        mTargetClass = targetClass;
-        mTargetClassMethods = targetClass.getMethods();
-        mTarget = target;
-    }
-
-    /**
-     * Invoke a method by its name.
-     *
-     * <p>If more than one method exists in {@code targetClass}, the first method with the right
-     * number of arguments will be used, and later calls will all use that method.</p>
-     *
-     * @param methodName
-     *          The name of the method, which will be matched 1:1 to the destination method
-     * @param params
-     *          Variadic parameter list.
-     * @return
-     *          The same kind of value that would normally be returned by calling {@code methodName}
-     *          statically.
-     *
-     * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
-     * @throws Throwable will rethrow anything that the target method would normally throw
-     */
-    @SuppressWarnings("unchecked")
-    public <K> K invoke(String methodName, Object... params) {
-        checkNotNull(methodName, "methodName must not be null");
-
-        Method targetMethod = mMethods.get(methodName);
-        if (targetMethod == null) {
-            for (Method method : mTargetClassMethods) {
-                // TODO future: match types of params if possible
-                if (method.getName().equals(methodName) &&
-                        (params.length == method.getParameterTypes().length) ) {
-                    targetMethod = method;
-                    mMethods.put(methodName, targetMethod);
-                    break;
-                }
-            }
-
-            if (targetMethod == null) {
-                throw new IllegalArgumentException(
-                        "Method " + methodName + " does not exist on class " + mTargetClass);
-            }
-        }
-
-        try {
-            return (K) mTarget.dispatch(targetMethod, params);
-        } catch (Throwable e) {
-            UncheckedThrow.throwAnyException(e);
-            // unreachable
-            return null;
-        }
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
deleted file mode 100644
index fada075..0000000
--- a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-/**
- * Do nothing when dispatching; follows the null object pattern.
- */
-public class NullDispatcher<T> implements Dispatchable<T> {
-    /**
-     * Create a dispatcher that does nothing when dispatched to.
-     */
-    public NullDispatcher() {
-    }
-
-    /**
-     * Do nothing; all parameters are ignored.
-     */
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        return null;
-    }
-}
diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html
deleted file mode 100644
index 783d0a1..0000000
--- a/core/java/android/hardware/camera2/dispatch/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-{@hide}
-</body>
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index c9eecf1..9e4cb80 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,16 +15,17 @@
  */
 package android.hardware.camera2.impl;
 
+import android.os.Binder;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.dispatch.Dispatchable;
-import android.hardware.camera2.dispatch.MethodNameInvoker;
 import android.view.Surface;
 
+import java.util.concurrent.Executor;
+
 import static com.android.internal.util.Preconditions.*;
 
 /**
@@ -34,164 +35,86 @@
  * to use our own proxy mechanism.</p>
  */
 public class CallbackProxies {
-
-    // TODO: replace with codegen
-
-    public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
-        private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
-
-        public DeviceStateCallbackProxy(
-                Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
-        }
-
-        @Override
-        public void onOpened(CameraDevice camera) {
-            mProxy.invoke("onOpened", camera);
-        }
-
-        @Override
-        public void onDisconnected(CameraDevice camera) {
-            mProxy.invoke("onDisconnected", camera);
-        }
-
-        @Override
-        public void onError(CameraDevice camera, int error) {
-            mProxy.invoke("onError", camera, error);
-        }
-
-        @Override
-        public void onUnconfigured(CameraDevice camera) {
-            mProxy.invoke("onUnconfigured", camera);
-        }
-
-        @Override
-        public void onActive(CameraDevice camera) {
-            mProxy.invoke("onActive", camera);
-        }
-
-        @Override
-        public void onBusy(CameraDevice camera) {
-            mProxy.invoke("onBusy", camera);
-        }
-
-        @Override
-        public void onClosed(CameraDevice camera) {
-            mProxy.invoke("onClosed", camera);
-        }
-
-        @Override
-        public void onIdle(CameraDevice camera) {
-            mProxy.invoke("onIdle", camera);
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
-        private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
-
-        public DeviceCaptureCallbackProxy(
-                Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
-        }
-
-        @Override
-        public void onCaptureStarted(CameraDevice camera,
-                CaptureRequest request, long timestamp, long frameNumber) {
-            mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
-        }
-
-        @Override
-        public void onCapturePartial(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
-            mProxy.invoke("onCapturePartial", camera, request, result);
-        }
-
-        @Override
-        public void onCaptureProgressed(CameraDevice camera,
-                CaptureRequest request, CaptureResult partialResult) {
-            mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
-        }
-
-        @Override
-        public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, TotalCaptureResult result) {
-            mProxy.invoke("onCaptureCompleted", camera, request, result);
-        }
-
-        @Override
-        public void onCaptureFailed(CameraDevice camera,
-                CaptureRequest request, CaptureFailure failure) {
-            mProxy.invoke("onCaptureFailed", camera, request, failure);
-        }
-
-        @Override
-        public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, long frameNumber) {
-            mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
-        }
-
-        @Override
-        public void onCaptureSequenceAborted(CameraDevice camera,
-                int sequenceId) {
-            mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
-        }
-
-        @Override
-        public void onCaptureBufferLost(CameraDevice camera,
-                CaptureRequest request, Surface target, long frameNumber) {
-            mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
-        }
-
-    }
-
     public static class SessionStateCallbackProxy
             extends CameraCaptureSession.StateCallback {
-        private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
+        private final Executor mExecutor;
+        private final CameraCaptureSession.StateCallback mCallback;
 
-        public SessionStateCallbackProxy(
-                Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget,
-                    CameraCaptureSession.StateCallback.class);
+        public SessionStateCallbackProxy(Executor executor,
+                CameraCaptureSession.StateCallback callback) {
+            mExecutor = checkNotNull(executor, "executor must not be null");
+            mCallback = checkNotNull(callback, "callback must not be null");
         }
 
         @Override
         public void onConfigured(CameraCaptureSession session) {
-            mProxy.invoke("onConfigured", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConfigured(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
 
         @Override
         public void onConfigureFailed(CameraCaptureSession session) {
-            mProxy.invoke("onConfigureFailed", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConfigureFailed(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onReady(CameraCaptureSession session) {
-            mProxy.invoke("onReady", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onReady(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onActive(CameraCaptureSession session) {
-            mProxy.invoke("onActive", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onActive(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onCaptureQueueEmpty(CameraCaptureSession session) {
-            mProxy.invoke("onCaptureQueueEmpty", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onClosed(CameraCaptureSession session) {
-            mProxy.invoke("onClosed", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onClosed(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
-            mProxy.invoke("onSurfacePrepared", session, surface);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 8b8bbc3..9cac71c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,20 +20,17 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
-import android.hardware.camera2.dispatch.BroadcastDispatcher;
-import android.hardware.camera2.dispatch.DuckTypingDispatcher;
-import android.hardware.camera2.dispatch.HandlerDispatcher;
-import android.hardware.camera2.dispatch.InvokeDispatcher;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.TaskDrainer;
 import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Binder;
 import android.os.Handler;
 import android.util.Log;
 import android.view.Surface;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
 import static com.android.internal.util.Preconditions.*;
@@ -51,16 +48,16 @@
     private final Surface mInput;
     /**
      * User-specified state callback, used for outgoing events; calls to this object will be
-     * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+     * automatically invoked via {@code mStateExecutor}.
      */
     private final CameraCaptureSession.StateCallback mStateCallback;
-    /** User-specified state handler used for outgoing state callback events */
-    private final Handler mStateHandler;
+    /** User-specified state executor used for outgoing state callback events */
+    private final Executor mStateExecutor;
 
     /** Internal camera device; used to translate calls into existing deprecated API */
     private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
-    /** Internal handler; used for all incoming events to preserve total order */
-    private final Handler mDeviceHandler;
+    /** Internal executor; used for all incoming events to preserve total order */
+    private final Executor mDeviceExecutor;
 
     /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
     private final TaskDrainer<Integer> mSequenceDrainer;
@@ -87,9 +84,9 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraCaptureSessionImpl(int id, Surface input,
-            CameraCaptureSession.StateCallback callback, Handler stateHandler,
+            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
-            Handler deviceStateHandler, boolean configureSuccess) {
+            Executor deviceStateExecutor, boolean configureSuccess) {
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
@@ -98,10 +95,11 @@
         mIdString = String.format("Session %d: ", mId);
 
         mInput = input;
-        mStateHandler = checkHandler(stateHandler);
-        mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
+        mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
+        mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
 
-        mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+        mDeviceExecutor = checkNotNull(deviceStateExecutor,
+                "deviceStateExecutor must not be null");
         mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
 
         /*
@@ -110,11 +108,11 @@
          * This ensures total ordering between CameraDevice.StateCallback and
          * CameraDeviceImpl.CaptureCallback events.
          */
-        mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+        mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
                 /*name*/"seq");
-        mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+        mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
                 /*name*/"idle");
-        mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+        mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
                 /*name*/"abort");
 
         // CameraDevice should call configureOutputs and have it finish before constructing us
@@ -180,7 +178,7 @@
             }
 
             return addPendingSequence(mDeviceImpl.capture(request,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
         }
     }
 
@@ -217,7 +215,7 @@
             }
 
             return addPendingSequence(mDeviceImpl.captureBurst(requests,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
         }
     }
 
@@ -241,7 +239,7 @@
             }
 
             return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
         }
     }
 
@@ -274,7 +272,7 @@
             }
 
             return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
         }
     }
 
@@ -446,119 +444,145 @@
     }
 
     /**
-     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
+     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
      */
-    private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
-        InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
-        HandlerDispatcher<StateCallback> handlerPassthrough =
-                new HandlerDispatcher<>(userCallbackSink, handler);
-
-        return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
+    private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
+        return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
     }
 
     /**
      * Forward callbacks from
      * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
      *
-     * <p>In particular, all calls are automatically split to go both to our own
-     * internal callback, and to the user-specified callback (by transparently posting
-     * to the user-specified handler).</p>
-     *
      * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
      */
     @SuppressWarnings("deprecation")
     private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
             Handler handler, CaptureCallback callback) {
-        CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+        final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
+                handler) : null;
 
+        return new CameraDeviceImpl.CaptureCallback() {
             @Override
             public void onCaptureStarted(CameraDevice camera,
                     CaptureRequest request, long timestamp, long frameNumber) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureStarted(
+                                    CameraCaptureSessionImpl.this, request, timestamp,
+                                    frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCapturePartial(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult result) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCapturePartial(
+                                    CameraCaptureSessionImpl.this, request, result));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureProgressed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureProgressed(
+                                    CameraCaptureSessionImpl.this, request, partialResult));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureCompleted(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureCompleted(
+                                    CameraCaptureSessionImpl.this, request, result));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureFailed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureFailed(
+                                    CameraCaptureSessionImpl.this, request, failure));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureSequenceCompleted(CameraDevice camera,
                     int sequenceId, long frameNumber) {
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureSequenceCompleted(
+                                    CameraCaptureSessionImpl.this, sequenceId, frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureSequenceAborted(CameraDevice camera,
                     int sequenceId) {
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureSequenceAborted(
+                                    CameraCaptureSessionImpl.this, sequenceId));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureBufferLost(CameraDevice camera,
                     CaptureRequest request, Surface target, long frameNumber) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureBufferLost(
+                                    CameraCaptureSessionImpl.this, request, target, frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
-
         };
-
-        /*
-         * Split the calls from the device callback into local callback and the following chain:
-         * - replace the first CameraDevice arg with a CameraCaptureSession
-         * - duck type from device callback to session callback
-         * - then forward the call to a handler
-         * - then finally invoke the destination method on the session callback object
-         */
-        if (callback == null) {
-            // OK: API allows the user to not specify a callback, and the handler may
-            // also be null in that case. Collapse whole dispatch chain to only call the local
-            // callback
-            return localCallback;
-        }
-
-        InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
-                new InvokeDispatcher<>(localCallback);
-
-        InvokeDispatcher<CaptureCallback> userCallbackSink =
-                new InvokeDispatcher<>(callback);
-        HandlerDispatcher<CaptureCallback> handlerPassthrough =
-                new HandlerDispatcher<>(userCallbackSink, handler);
-        DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
-                = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
-        ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
-                replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
-                        /*argumentIndex*/0, this);
-
-        BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
-                new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
-                    replaceDeviceWithSession,
-                    localSink);
-
-        return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
     }
 
     /**
      *
-     * Create an internal state callback, to be invoked on the mDeviceHandler
+     * Create an internal state callback, to be invoked on the mDeviceExecutor
      *
      * <p>It has a few behaviors:
      * <ul>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 06c2c25..89f6172 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,6 +33,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -59,14 +60,14 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraConstrainedHighSpeedCaptureSessionImpl(int id,
-            CameraCaptureSession.StateCallback callback, Handler stateHandler,
+            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
-            Handler deviceStateHandler, boolean configureSuccess,
+            Executor deviceStateExecutor, boolean configureSuccess,
             CameraCharacteristics characteristics) {
         mCharacteristics = characteristics;
         CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
         mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
-                stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+                stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
     }
 
     @Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 511fa43..1f35f31 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
+import android.annotation.NonNull;
 import android.hardware.ICameraService;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
@@ -35,6 +36,7 @@
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -47,6 +49,8 @@
 import android.util.SparseArray;
 import android.view.Surface;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -58,6 +62,8 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executor;
+
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,7 +84,7 @@
 
     private final StateCallback mDeviceCallback;
     private volatile StateCallbackKK mSessionStateCallback;
-    private final Handler mDeviceHandler;
+    private final Executor mDeviceExecutor;
 
     private final AtomicBoolean mClosing = new AtomicBoolean();
     private boolean mInError = false;
@@ -241,7 +247,7 @@
         }
         mCameraId = cameraId;
         mDeviceCallback = callback;
-        mDeviceHandler = handler;
+        mDeviceExecutor = checkAndWrapHandler(handler);
         mCharacteristics = characteristics;
         mAppTargetSdkVersion = appTargetSdkVersion;
 
@@ -288,15 +294,15 @@
                 try {
                     remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
                 } catch (RemoteException e) {
-                    CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+                    CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
 
                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                             "The camera device has encountered a serious error");
                 }
             }
 
-            mDeviceHandler.post(mCallOnOpened);
-            mDeviceHandler.post(mCallOnUnconfigured);
+            mDeviceExecutor.execute(mCallOnOpened);
+            mDeviceExecutor.execute(mCallOnUnconfigured);
         }
     }
 
@@ -335,7 +341,7 @@
         final boolean isError = failureIsError;
         synchronized(mInterfaceLock) {
             mInError = true;
-            mDeviceHandler.post(new Runnable() {
+            mDeviceExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     if (isError) {
@@ -423,7 +429,7 @@
                 }
             }
 
-            mDeviceHandler.post(mCallOnBusy);
+            mDeviceExecutor.execute(mCallOnBusy);
             stopRepeating();
 
             try {
@@ -482,10 +488,10 @@
                 throw e;
             } finally {
                 if (success && outputs.size() > 0) {
-                    mDeviceHandler.post(mCallOnIdle);
+                    mDeviceExecutor.execute(mCallOnIdle);
                 } else {
                     // Always return to the 'unconfigured' state if we didn't hit a fatal error
-                    mDeviceHandler.post(mCallOnUnconfigured);
+                    mDeviceExecutor.execute(mCallOnUnconfigured);
                 }
             }
         }
@@ -501,8 +507,9 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback, handler,
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+        createCaptureSessionInternal(null, outConfigurations, callback,
+                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -517,7 +524,7 @@
         // OutputConfiguration objects are immutable, but need to have our own array
         List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
 
-        createCaptureSessionInternal(null, currentOutputs, callback, handler,
+        createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
                 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
     }
 
@@ -537,8 +544,9 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, outConfigurations, callback,
+                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -566,8 +574,8 @@
             currentOutputs.add(new OutputConfiguration(output));
         }
         createCaptureSessionInternal(inputConfig, currentOutputs,
-                callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
-                /*sessionParams*/ null);
+                callback, checkAndWrapHandler(handler),
+                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
     }
 
     @Override
@@ -582,7 +590,8 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback, handler,
+        createCaptureSessionInternal(null, outConfigurations, callback,
+                checkAndWrapHandler(handler),
                 /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
                 /*sessionParams*/ null);
     }
@@ -597,8 +606,8 @@
         for (OutputConfiguration output : outputs) {
             currentOutputs.add(new OutputConfiguration(output));
         }
-        createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
-                /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, currentOutputs, callback,
+                checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
     }
 
     @Override
@@ -612,14 +621,17 @@
         if (outputConfigs == null) {
             throw new IllegalArgumentException("Invalid output configurations");
         }
+        if (config.getExecutor() == null) {
+            throw new IllegalArgumentException("Invalid executor");
+        }
         createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
-                config.getStateCallback(), config.getHandler(), config.getSessionType(),
+                config.getStateCallback(), config.getExecutor(), config.getSessionType(),
                 config.getSessionParameters());
     }
 
     private void createCaptureSessionInternal(InputConfiguration inputConfig,
             List<OutputConfiguration> outputConfigurations,
-            CameraCaptureSession.StateCallback callback, Handler handler,
+            CameraCaptureSession.StateCallback callback, Executor executor,
             int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
@@ -673,12 +685,11 @@
                 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
 
                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
-                        callback, handler, this, mDeviceHandler, configureSuccess,
+                        callback, executor, this, mDeviceExecutor, configureSuccess,
                         mCharacteristics);
             } else {
                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
-                        callback, handler, this, mDeviceHandler,
-                        configureSuccess);
+                        callback, executor, this, mDeviceExecutor, configureSuccess);
             }
 
             // TODO: wait until current session closes, then create the new session
@@ -893,22 +904,22 @@
         }
     }
 
-    public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
+    public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
             throws CameraAccessException {
         if (DEBUG) {
             Log.d(TAG, "calling capture");
         }
         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
         requestList.add(request);
-        return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
+        return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
     }
 
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         if (requests == null || requests.isEmpty()) {
             throw new IllegalArgumentException("At least one request must be given");
         }
-        return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
+        return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
     }
 
     /**
@@ -963,7 +974,12 @@
                         }
                     }
                 };
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             } else {
                 Log.w(TAG, String.format(
                         "did not register callback to request %d",
@@ -982,11 +998,11 @@
     }
 
     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
-            Handler handler, boolean repeating) throws CameraAccessException {
+            Executor executor, boolean repeating) throws CameraAccessException {
 
-        // Need a valid handler, or current thread needs to have a looper, if
+        // Need a valid executor, or current thread needs to have a looper, if
         // callback is valid
-        handler = checkHandler(handler, callback);
+        executor = checkExecutor(executor, callback);
 
         // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
         // the surface isn't a physical stream surface for reprocessing request
@@ -1040,7 +1056,7 @@
             if (callback != null) {
                 mCaptureCallbackMap.put(requestInfo.getRequestId(),
                         new CaptureCallbackHolder(
-                            callback, requestList, handler, repeating, mNextSessionId - 1));
+                            callback, requestList, executor, repeating, mNextSessionId - 1));
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1059,7 +1075,7 @@
             }
 
             if (mIdle) {
-                mDeviceHandler.post(mCallOnActive);
+                mDeviceExecutor.execute(mCallOnActive);
             }
             mIdle = false;
 
@@ -1068,18 +1084,18 @@
     }
 
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
         requestList.add(request);
-        return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
+        return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
     }
 
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         if (requests == null || requests.isEmpty()) {
             throw new IllegalArgumentException("At least one request must be given");
         }
-        return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
+        return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
     }
 
     public void stopRepeating() throws CameraAccessException {
@@ -1124,12 +1140,12 @@
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
 
-            mDeviceHandler.post(mCallOnBusy);
+            mDeviceExecutor.execute(mCallOnBusy);
 
             // If already idle, just do a busy->idle transition immediately, don't actually
             // flush.
             if (mIdle) {
-                mDeviceHandler.post(mCallOnIdle);
+                mDeviceExecutor.execute(mCallOnIdle);
                 return;
             }
 
@@ -1157,7 +1173,7 @@
             // either a normal close where the remote device is valid
             // or a close after a startup error (no remote device but in error state)
             if (mRemoteDevice != null || mInError) {
-                mDeviceHandler.post(mCallOnClosed);
+                mDeviceExecutor.execute(mCallOnClosed);
             }
 
             mRemoteDevice = null;
@@ -1354,7 +1370,7 @@
         private final boolean mRepeating;
         private final CaptureCallback mCallback;
         private final List<CaptureRequest> mRequestList;
-        private final Handler mHandler;
+        private final Executor mExecutor;
         private final int mSessionId;
         /**
          * <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1366,13 +1382,13 @@
         private final boolean mHasBatchedOutputs;
 
         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
-                Handler handler, boolean repeating, int sessionId) {
-            if (callback == null || handler == null) {
+                Executor executor, boolean repeating, int sessionId) {
+            if (callback == null || executor == null) {
                 throw new UnsupportedOperationException(
                     "Must have a valid handler and a valid callback");
             }
             mRepeating = repeating;
-            mHandler = handler;
+            mExecutor = executor;
             mRequestList = new ArrayList<CaptureRequest>(requestList);
             mCallback = callback;
             mSessionId = sessionId;
@@ -1425,8 +1441,8 @@
             return getRequest(0);
         }
 
-        public Handler getHandler() {
-            return mHandler;
+        public Executor getExecutor() {
+            return mExecutor;
         }
 
         public int getSessionId() {
@@ -1810,7 +1826,12 @@
                         }
                     }
                 };
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
     }
@@ -1838,7 +1859,12 @@
 
                 switch (errorCode) {
                     case ERROR_CAMERA_DISCONNECTED:
-                        CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
                         break;
                     case ERROR_CAMERA_REQUEST:
                     case ERROR_CAMERA_RESULT:
@@ -1860,8 +1886,13 @@
 
         private void scheduleNotifyError(int code) {
             mInError = true;
-            CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
-                    CameraDeviceCallbacks::notifyError, this, code));
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
+                            CameraDeviceCallbacks::notifyError, this, code));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         private void notifyError(int code) {
@@ -1900,7 +1931,12 @@
                 if (mRemoteDevice == null) return; // Camera already closed
 
                 if (!CameraDeviceImpl.this.mIdle) {
-                    CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 }
                 CameraDeviceImpl.this.mIdle = true;
             }
@@ -1929,36 +1965,41 @@
                 if (isClosed()) return;
 
                 // Dispatch capture start notice
-                holder.getHandler().post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            if (!CameraDeviceImpl.this.isClosed()) {
-                                final int subsequenceId = resultExtras.getSubsequenceId();
-                                final CaptureRequest request = holder.getRequest(subsequenceId);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                if (!CameraDeviceImpl.this.isClosed()) {
+                                    final int subsequenceId = resultExtras.getSubsequenceId();
+                                    final CaptureRequest request = holder.getRequest(subsequenceId);
 
-                                if (holder.hasBatchedOutputs()) {
-                                    // Send derived onCaptureStarted for requests within the batch
-                                    final Range<Integer> fpsRange =
-                                        request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
-                                    for (int i = 0; i < holder.getRequestCount(); i++) {
+                                    if (holder.hasBatchedOutputs()) {
+                                        // Send derived onCaptureStarted for requests within the
+                                        // batch
+                                        final Range<Integer> fpsRange =
+                                            request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+                                        for (int i = 0; i < holder.getRequestCount(); i++) {
+                                            holder.getCallback().onCaptureStarted(
+                                                CameraDeviceImpl.this,
+                                                holder.getRequest(i),
+                                                timestamp - (subsequenceId - i) *
+                                                NANO_PER_SECOND/fpsRange.getUpper(),
+                                                frameNumber - (subsequenceId - i));
+                                        }
+                                    } else {
                                         holder.getCallback().onCaptureStarted(
                                             CameraDeviceImpl.this,
-                                            holder.getRequest(i),
-                                            timestamp - (subsequenceId - i) *
-                                            NANO_PER_SECOND/fpsRange.getUpper(),
-                                            frameNumber - (subsequenceId - i));
+                                            holder.getRequest(resultExtras.getSubsequenceId()),
+                                            timestamp, frameNumber);
                                     }
-                                } else {
-                                    holder.getCallback().onCaptureStarted(
-                                        CameraDeviceImpl.this,
-                                        holder.getRequest(resultExtras.getSubsequenceId()),
-                                        timestamp, frameNumber);
                                 }
                             }
-                        }
-                    });
-
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
 
@@ -2111,7 +2152,12 @@
                     finalResult = resultAsCapture;
                 }
 
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
 
                 // Collect the partials for a total result; or mark the frame as totally completed
                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2207,7 +2253,12 @@
                         }
                     };
                     // Dispatch the failure callback
-                    holder.getHandler().post(failureDispatch);
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        holder.getExecutor().execute(failureDispatch);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 }
             } else {
                 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2247,7 +2298,12 @@
                 checkAndFireSequenceComplete();
 
                 // Dispatch the failure callback
-                holder.getHandler().post(failureDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(failureDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
 
         }
@@ -2255,6 +2311,62 @@
     } // public class CameraDeviceCallbacks
 
     /**
+     * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
+     * {@link Handler}.
+     *
+     * @hide
+     */
+    private static class CameraHandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public CameraHandlerExecutor(@NonNull Handler handler) {
+            mHandler = Preconditions.checkNotNull(handler);
+        }
+
+        @Override
+        public void execute(Runnable command) {
+            // Return value of 'post()' will be ignored in order to keep the
+            // same camera behavior. For further details see b/74605221 .
+            mHandler.post(command);
+        }
+    }
+
+    /**
+     * Default executor management.
+     *
+     * <p>
+     * If executor is null, get the current thread's
+     * Looper to create a Executor with. If no looper exists, throw
+     * {@code IllegalArgumentException}.
+     * </p>
+     */
+    static Executor checkExecutor(Executor executor) {
+        return (executor == null) ? checkAndWrapHandler(null) : executor;
+    }
+
+    /**
+     * Default executor management.
+     *
+     * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
+     */
+    static <T> Executor checkExecutor(Executor executor, T callback) {
+        return (callback != null) ? checkExecutor(executor) : executor;
+    }
+
+    /**
+     * Wrap Handler in Executor.
+     *
+     * <p>
+     * If handler is null, get the current thread's
+     * Looper to create a Executor with. If no looper exists, throw
+     * {@code IllegalArgumentException}.
+     * </p>
+     */
+    public static Executor checkAndWrapHandler(Handler handler) {
+        return new CameraHandlerExecutor(checkHandler(handler));
+    }
+
+    /**
      * Default handler management.
      *
      * <p>
@@ -2328,6 +2440,11 @@
                 }
             }
         };
-        CameraDeviceImpl.this.mDeviceHandler.post(r);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            CameraDeviceImpl.this.mDeviceExecutor.execute(r);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 }
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index a79a6c1..7bdb4a2 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.IntDef;
-import android.os.Handler;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -31,6 +31,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -78,7 +79,7 @@
     private List<OutputConfiguration> mOutputConfigurations;
     private CameraCaptureSession.StateCallback mStateCallback;
     private int mSessionType;
-    private Handler mHandler = null;
+    private Executor mExecutor = null;
     private InputConfiguration mInputConfig = null;
     private CaptureRequest mSessionParameters = null;
 
@@ -87,10 +88,9 @@
      *
      * @param sessionType The session type.
      * @param outputs A list of output configurations for the capture session.
+     * @param executor The executor which should be used to invoke the callback. In general it is
+     *                 recommended that camera operations are not done on the main (UI) thread.
      * @param cb A state callback interface implementation.
-     * @param handler The handler on which the callback will be invoked. If it is
-     *                set to null, the callback will be invoked on the current thread's
-     *                {@link android.os.Looper looper}.
      *
      * @see #SESSION_REGULAR
      * @see #SESSION_HIGH_SPEED
@@ -101,11 +101,12 @@
      */
     public SessionConfiguration(@SessionMode int sessionType,
             @NonNull List<OutputConfiguration> outputs,
-            @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CameraCaptureSession.StateCallback cb) {
         mSessionType = sessionType;
         mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
         mStateCallback = cb;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     /**
@@ -136,14 +137,12 @@
     }
 
     /**
-     * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+     * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
      *
-     * @return The handler on which the callback will be invoked. If it is
-     *         set to null, the callback will be invoked on the current thread's
-     *         {@link android.os.Looper looper}.
+     * @return The Executor on which the callback will be invoked.
      */
-    public Handler getHandler() {
-        return mHandler;
+    public Executor getExecutor() {
+        return mExecutor;
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java
index ed30ff3..e71f26a 100644
--- a/core/java/android/hardware/camera2/utils/TaskDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
  */
 package android.hardware.camera2.utils;
 
-import android.os.Handler;
 import android.util.Log;
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -55,7 +55,7 @@
     private static final String TAG = "TaskDrainer";
     private final boolean DEBUG = false;
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final DrainListener mListener;
     private final String mName;
 
@@ -73,28 +73,27 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskDrainer(Handler handler, DrainListener listener) {
-        mHandler = checkNotNull(handler, "handler must not be null");
+    public TaskDrainer(Executor executor, DrainListener listener) {
+        mExecutor = checkNotNull(executor, "executor must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = null;
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskDrainer(Handler handler, DrainListener listener, String name) {
-        // XX: Probably don't need a handler at all here
-        mHandler = checkNotNull(handler, "handler must not be null");
+    public TaskDrainer(Executor executor, DrainListener listener, String name) {
+        mExecutor = checkNotNull(executor, "executor must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = name;
     }
@@ -200,15 +199,12 @@
     }
 
     private void postDrained() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
+        mExecutor.execute(() -> {
                 if (DEBUG) {
                     Log.v(TAG + "[" + mName + "]", "onDrained");
                 }
 
                 mListener.onDrained();
-            }
         });
     }
 }
diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
index f6272c9..9615450 100644
--- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,7 +16,8 @@
 package android.hardware.camera2.utils;
 
 import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-import android.os.Handler;
+
+import java.util.concurrent.Executor;
 
 /**
  * Keep track of a single concurrent task starting and finishing;
@@ -38,25 +39,25 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskSingleDrainer(Handler handler, DrainListener listener) {
-        mTaskDrainer = new TaskDrainer<>(handler, listener);
+    public TaskSingleDrainer(Executor executor, DrainListener listener) {
+        mTaskDrainer = new TaskDrainer<>(executor, listener);
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
-        mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+    public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
+        mTaskDrainer = new TaskDrainer<>(executor, listener, name);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e81fbee..a3b2d22 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -668,6 +668,45 @@
     }
 
     /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    public BrightnessConfiguration getBrightnessConfiguration() {
+        return getBrightnessConfigurationForUser(mContext.getUserId());
+    }
+
+    /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set
+     * for a specific user.
+     *
+     * Note this requires the INTERACT_ACROSS_USERS permission if getting the configuration for a
+     * user other than the one you're currently running as.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        return mGlobal.getBrightnessConfigurationForUser(userId);
+    }
+
+    /**
+     * Gets the default global display brightness configuration or null one hasn't
+     * been configured.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    @Nullable
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return mGlobal.getDefaultBrightnessConfiguration();
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d7f7c86..1f67a6b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -490,6 +490,32 @@
     }
 
     /**
+     * Gets the global brightness configuration for a given user or null if one hasn't been set.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        try {
+            return mDm.getBrightnessConfigurationForUser(userId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the default brightness configuration or null if one hasn't been configured.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        try {
+            return mDm.getDefaultBrightnessConfiguration();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 0571ae1..9fcb9d3 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -96,6 +96,14 @@
     void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId,
             String packageName);
 
+    // Gets the global brightness configuration for a given user. Requires
+    // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not
+    // the same as the calling user.
+    BrightnessConfiguration getBrightnessConfigurationForUser(int userId);
+
+    // Gets the default brightness configuration if configured.
+    BrightnessConfiguration getDefaultBrightnessConfiguration();
+
     // Temporarily sets the display brightness.
     void setTemporaryBrightness(int brightness);
 
diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl
deleted file mode 100644
index 2ea4d23..0000000
--- a/core/java/android/hardware/location/IFusedLocationHardware.aidl
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2013, 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/license/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.location;
-
-import android.hardware.location.IFusedLocationHardwareSink;
-import android.location.FusedBatchOptions;
-
-/**
- * Fused Location hardware interface.
- * This interface is the basic set of supported functionality by Fused Hardware
- * modules that offer Location batching capabilities.
- *
- * @hide
- */
-interface IFusedLocationHardware {
-    /**
-     * Registers a sink with the Location Hardware object.
-     *
-     * @param eventSink     The sink to register.
-     */
-    void registerSink(in IFusedLocationHardwareSink eventSink) = 0;
-
-    /**
-     * Unregisters a sink with the Location Hardware object.
-     *
-     * @param eventSink     The sink to unregister.
-     */
-    void unregisterSink(in IFusedLocationHardwareSink eventSink) = 1;
-
-    /**
-     * Provides access to the batch size available in Hardware.
-     *
-     * @return The batch size the hardware supports.
-     */
-    int getSupportedBatchSize() = 2;
-
-    /**
-     * Requests the Hardware to start batching locations.
-     *
-     * @param id            An Id associated with the request.
-     * @param batchOptions  The options required for batching.
-     *
-     * @throws RuntimeException if the request Id exists.
-     */
-    void startBatching(in int id, in FusedBatchOptions batchOptions) = 3;
-
-    /**
-     * Requests the Hardware to stop batching for the given Id.
-     *
-     * @param id    The request that needs to be stopped.
-     * @throws RuntimeException if the request Id is unknown.
-     */
-    void stopBatching(in int id) = 4;
-
-    /**
-     * Updates a batching operation in progress.
-     *
-     * @param id                The Id of the operation to update.
-     * @param batchOptions     The options to apply to the given operation.
-     *
-     * @throws RuntimeException if the Id of the request is unknown.
-     */
-    void updateBatchingOptions(in int id, in FusedBatchOptions batchOptions) = 5;
-
-    /**
-     * Requests the most recent locations available in Hardware.
-     * This operation does not dequeue the locations, so still other batching
-     * events will continue working.
-     *
-     * @param batchSizeRequested    The number of locations requested.
-     */
-    void requestBatchOfLocations(in int batchSizeRequested) = 6;
-
-    /**
-     * Flags if the Hardware supports injection of diagnostic data.
-     *
-     * @return True if data injection is supported, false otherwise.
-     */
-    boolean supportsDiagnosticDataInjection() = 7;
-
-    /**
-     * Injects diagnostic data into the Hardware subsystem.
-     *
-     * @param data  The data to inject.
-     * @throws RuntimeException if injection is not supported.
-     */
-    void injectDiagnosticData(in String data) = 8;
-
-    /**
-     * Flags if the Hardware supports injection of device context information.
-     *
-     * @return True if device context injection is supported, false otherwise.
-     */
-    boolean supportsDeviceContextInjection() = 9;
-
-    /**
-     * Injects device context information into the Hardware subsystem.
-     *
-     * @param deviceEnabledContext  The context to inject.
-     * @throws RuntimeException if injection is not supported.
-     */
-    void injectDeviceContext(in int deviceEnabledContext) = 10;
-
-    /**
-     * Requests all batched locations currently available in Hardware
-     * and clears the buffer.  Any subsequent calls will not return any
-     * of the locations returned in this call.
-     */
-    void flushBatchedLocations() = 11;
-
-    /**
-     * Returns the version of this FLP HAL implementation.
-     */
-    int getVersion() = 12;
-}
diff --git a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
deleted file mode 100644
index a7dd035..0000000
--- a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2013, 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/license/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.location;
-
-import android.location.Location;
-
-/**
- * Fused Location hardware event sink interface.
- * This interface defines the set of events that the FusedLocationHardware provides.
- *
- * @hide
- */
-oneway interface IFusedLocationHardwareSink {
-    /**
-     * Event generated when a batch of location information is available.
-     *
-     * @param locations     The batch of location information available.
-     */
-    void onLocationAvailable(in Location[] locations) = 0;
-
-    /**
-     * Event generated from FLP HAL to provide diagnostic data to the platform.
-     *
-     * @param data      The diagnostic data provided by FLP HAL.
-     */
-    void onDiagnosticDataAvailable(in String data) = 1;
-
-    /**
-     * Event generated from FLP HAL to provide a mask of supported
-     * capabilities.  Should be called immediatly after init.
-     */
-    void onCapabilities(int capabilities) = 2;
-
-    /**
-     * Event generated from FLP HAL when the status of location batching
-     * changes (location is successful/unsuccessful).
-     */
-    void onStatusChanged(int status) = 3;
-}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b635088..dde8a33 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,14 @@
 
 package android.hardware.soundtrigger;
 
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.EPIPE;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.media.AudioFormat;
 import android.os.Handler;
 import android.os.Parcel;
@@ -25,22 +33,33 @@
 import java.util.Arrays;
 import java.util.UUID;
 
-import static android.system.OsConstants.*;
-
 /**
  * The SoundTrigger class provides access via JNI to the native service managing
  * the sound trigger HAL.
  *
  * @hide
  */
+@SystemApi
 public class SoundTrigger {
 
+    private SoundTrigger() {
+    }
+
+    /**
+     * Status code used when the operation succeeded
+     */
     public static final int STATUS_OK = 0;
+    /** @hide */
     public static final int STATUS_ERROR = Integer.MIN_VALUE;
+    /** @hide */
     public static final int STATUS_PERMISSION_DENIED = -EPERM;
+    /** @hide */
     public static final int STATUS_NO_INIT = -ENODEV;
+    /** @hide */
     public static final int STATUS_BAD_VALUE = -EINVAL;
+    /** @hide */
     public static final int STATUS_DEAD_OBJECT = -EPIPE;
+    /** @hide */
     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
 
     /*****************************************************************************
@@ -48,6 +67,8 @@
      * managed by the native sound trigger service. Each module has a unique
      * ID used to target any API call to this paricular module. Module
      * properties are returned by listModules() method.
+     *
+     * @hide
      ****************************************************************************/
     public static class ModuleProperties implements Parcelable {
         /** Unique module ID provided by the native service */
@@ -187,6 +208,8 @@
      * implementation to detect a particular sound pattern.
      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
      * sound models.
+     *
+     * @hide
      ****************************************************************************/
     public static class SoundModel {
         /** Undefined sound model type */
@@ -261,6 +284,8 @@
     /*****************************************************************************
      * A Keyphrase describes a key phrase that can be detected by a
      * {@link KeyphraseSoundModel}
+     *
+     * @hide
      ****************************************************************************/
     public static class Keyphrase implements Parcelable {
         /** Unique identifier for this keyphrase */
@@ -382,6 +407,8 @@
      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
      * It contains data needed by the hardware to detect a certain number of key phrases
      * and the list of corresponding {@link Keyphrase} descriptors.
+     *
+     * @hide
      ****************************************************************************/
     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
         /** Key phrases in this sound model */
@@ -468,6 +495,8 @@
     /*****************************************************************************
      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
      * patterns.
+     *
+     * @hide
      ****************************************************************************/
     public static class GenericSoundModel extends SoundModel implements Parcelable {
 
@@ -524,52 +553,115 @@
     /**
      *  Modes for key phrase recognition
      */
-    /** Simple recognition of the key phrase */
+
+    /**
+     * Simple recognition of the key phrase
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
-    /** Trigger only if one user is identified */
+    /**
+     * Trigger only if one user is identified
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
-    /** Trigger only if one user is authenticated */
+    /**
+     * Trigger only if one user is authenticated
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
 
     /**
      *  Status codes for {@link RecognitionEvent}
      */
-    /** Recognition success */
+    /**
+     * Recognition success
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_SUCCESS = 0;
-    /** Recognition aborted (e.g. capture preempted by anotehr use case */
+    /**
+     * Recognition aborted (e.g. capture preempted by anotehr use case
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_ABORT = 1;
-    /** Recognition failure */
+    /**
+     * Recognition failure
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_FAILURE = 2;
 
     /**
      *  A RecognitionEvent is provided by the
-     *  {@link StatusListener#onRecognition(RecognitionEvent)}
+     *  {@code StatusListener#onRecognition(RecognitionEvent)}
      *  callback upon recognition success or failure.
      */
-    public static class RecognitionEvent implements Parcelable {
-        /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
+    public static class RecognitionEvent {
+        /**
+         * Recognition status e.g RECOGNITION_STATUS_SUCCESS
+         *
+         * @hide
+         */
         public final int status;
-        /** Sound Model corresponding to this event callback */
+        /**
+         *
+         * Sound Model corresponding to this event callback
+         *
+         * @hide
+         */
         public final int soundModelHandle;
-        /** True if it is possible to capture audio from this utterance buffered by the hardware */
+        /**
+         * True if it is possible to capture audio from this utterance buffered by the hardware
+         *
+         * @hide
+         */
         public final boolean captureAvailable;
-        /** Audio session ID to be used when capturing the utterance with an AudioRecord
-         * if captureAvailable() is true. */
+        /**
+         * Audio session ID to be used when capturing the utterance with an AudioRecord
+         * if captureAvailable() is true.
+         *
+         * @hide
+         */
         public final int captureSession;
-        /** Delay in ms between end of model detection and start of audio available for capture.
-         * A negative value is possible (e.g. if keyphrase is also available for capture) */
+        /**
+         * Delay in ms between end of model detection and start of audio available for capture.
+         * A negative value is possible (e.g. if keyphrase is also available for capture)
+         *
+         * @hide
+         */
         public final int captureDelayMs;
-        /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+        /**
+         * Duration in ms of audio captured before the start of the trigger. 0 if none.
+         *
+         * @hide
+         */
         public final int capturePreambleMs;
-        /** True if  the trigger (key phrase capture is present in binary data */
+        /**
+         * True if  the trigger (key phrase capture is present in binary data
+         *
+         * @hide
+         */
         public final boolean triggerInData;
-        /** Audio format of either the trigger in event data or to use for capture of the
-          * rest of the utterance */
-        public AudioFormat captureFormat;
-        /** Opaque data for use by system applications who know about voice engine internals,
-         * typically during enrollment. */
+        /**
+         * Audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @hide
+         */
+        public final AudioFormat captureFormat;
+        /**
+         * Opaque data for use by system applications who know about voice engine internals,
+         * typically during enrollment.
+         *
+         * @hide
+         */
         public final byte[] data;
 
+        /** @hide */
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                 int captureSession, int captureDelayMs, int capturePreambleMs,
                 boolean triggerInData, AudioFormat captureFormat, byte[] data) {
@@ -584,6 +676,46 @@
             this.data = data;
         }
 
+        /**
+         * Check if is possible to capture audio from this utterance buffered by the hardware.
+         *
+         * @return {@code true} iff a capturing is possible
+         */
+        public boolean isCaptureAvailable() {
+            return captureAvailable;
+        }
+
+        /**
+         * Get the audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @return the audio format
+         */
+        @Nullable public AudioFormat getCaptureFormat() {
+            return captureFormat;
+        }
+
+        /**
+         * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
+         * if {@link #isCaptureAvailable()} is true.
+         *
+         * @return The id of the capture session
+         */
+        public int getCaptureSession() {
+            return captureSession;
+        }
+
+        /**
+         * Get the opaque data for use by system applications who know about voice engine
+         * internals, typically during enrollment.
+         *
+         * @return The data of the event
+         */
+        public byte[] getData() {
+            return data;
+        }
+
+        /** @hide */
         public static final Parcelable.Creator<RecognitionEvent> CREATOR
                 = new Parcelable.Creator<RecognitionEvent>() {
             public RecognitionEvent createFromParcel(Parcel in) {
@@ -595,6 +727,7 @@
             }
         };
 
+        /** @hide */
         protected static RecognitionEvent fromParcel(Parcel in) {
             int status = in.readInt();
             int soundModelHandle = in.readInt();
@@ -619,12 +752,12 @@
                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
         }
 
-        @Override
+        /** @hide */
         public int describeContents() {
             return 0;
         }
 
-        @Override
+        /** @hide */
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(status);
             dest.writeInt(soundModelHandle);
@@ -726,6 +859,8 @@
      *  A RecognitionConfig is provided to
      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
      *  recognition request.
+     *
+     *  @hide
      */
     public static class RecognitionConfig implements Parcelable {
         /** True if the DSP should capture the trigger sound and make it available for further
@@ -744,7 +879,7 @@
         public final byte[] data;
 
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+                KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
             this.captureRequested = captureRequested;
             this.allowMultipleTriggers = allowMultipleTriggers;
             this.keyphrases = keyphrases;
@@ -799,6 +934,8 @@
      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
      * should trigger a recognition.
      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+     *
+     * @hide
      */
     public static class ConfidenceLevel implements Parcelable {
         public final int userId;
@@ -872,6 +1009,8 @@
     /**
      *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
      *  for a key phrase detection.
+     *
+     * @hide
      */
     public static class KeyphraseRecognitionExtra implements Parcelable {
         /** The keyphrase ID */
@@ -970,8 +1109,10 @@
 
     /**
      *  Specialized {@link RecognitionEvent} for a key phrase detection.
+     *
+     *  @hide
      */
-    public static class KeyphraseRecognitionEvent extends RecognitionEvent {
+    public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
@@ -1091,8 +1232,10 @@
     /**
      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
      * models(non-keyphrase). Currently does not contain any additional fields.
+     *
+     * @hide
      */
-    public static class GenericRecognitionEvent extends RecognitionEvent {
+    public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
         public GenericRecognitionEvent(int status, int soundModelHandle,
                 boolean captureAvailable, int captureSession, int captureDelayMs,
                 int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
@@ -1140,13 +1283,19 @@
     /**
      *  Status codes for {@link SoundModelEvent}
      */
-    /** Sound Model was updated */
+    /**
+     * Sound Model was updated
+     *
+     * @hide
+     */
     public static final int SOUNDMODEL_STATUS_UPDATED = 0;
 
     /**
      *  A SoundModelEvent is provided by the
      *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
      *  callback when a sound model has been updated by the implementation
+     *
+     *  @hide
      */
     public static class SoundModelEvent implements Parcelable {
         /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
@@ -1231,9 +1380,17 @@
      *  Native service state. {@link StatusListener#onServiceStateChange(int)}
      */
     // Keep in sync with system/core/include/system/sound_trigger.h
-    /** Sound trigger service is enabled */
+    /**
+     * Sound trigger service is enabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_ENABLED = 0;
-    /** Sound trigger service is disabled */
+    /**
+     * Sound trigger service is disabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_DISABLED = 1;
 
     /**
@@ -1245,6 +1402,8 @@
      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
      *         - {@link #STATUS_BAD_VALUE} if modules is null
      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+     *
+     * @hide
      */
     public static native int listModules(ArrayList <ModuleProperties> modules);
 
@@ -1256,6 +1415,8 @@
      * @param handler the Handler that will receive the callabcks. Can be null if default handler
      *                is OK.
      * @return a valid sound module in case of success or null in case of error.
+     *
+     * @hide
      */
     public static SoundTriggerModule attachModule(int moduleId,
                                                   StatusListener listener,
@@ -1270,6 +1431,8 @@
     /**
      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
      * to received recognition and error notifications.
+     *
+     * @hide
      */
     public static interface StatusListener {
         /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 36f359b..93b1b22 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1977,13 +1977,6 @@
      * services.jar, possibly in com.android.server.net. */
 
     /** {@hide} */
-    public static final boolean checkChangePermission(Context context) {
-        int uid = Binder.getCallingUid();
-        return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                .getPackageNameForUid(context, uid), false /* throwException */);
-    }
-
-    /** {@hide} */
     public static final void enforceChangePermission(Context context) {
         int uid = Binder.getCallingUid();
         Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3ce0283..3a3ddcc 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.IpSecConfig;
 import android.net.IpSecUdpEncapResponse;
@@ -48,11 +49,11 @@
 
     void addAddressToTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void removeAddressFromTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void deleteTunnelInterface(int resourceId);
 
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index c69a4d4..57f0588 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -38,6 +38,13 @@
     private static final String TAG = "IpSecAlgorithm";
 
     /**
+     * Null cipher.
+     *
+     * @hide
+     */
+    public static final String CRYPT_NULL = "ecb(cipher_null)";
+
+    /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for this key are {128, 192, 256}.
@@ -122,7 +129,7 @@
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key) {
+    public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
         this(algorithm, key, key.length * 8);
     }
 
@@ -137,7 +144,8 @@
      * @param key key padded to a multiple of 8 bits.
      * @param truncLenBits number of bits of output hash to use.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
+    public IpSecAlgorithm(
+            @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
         mName = algorithm;
         mKey = key.clone();
         mTruncLenBits = truncLenBits;
@@ -145,11 +153,13 @@
     }
 
     /** Get the algorithm name */
+    @NonNull
     public String getName() {
         return mName;
     }
 
     /** Get the key for this algorithm */
+    @NonNull
     public byte[] getKey() {
         return mKey.clone();
     }
@@ -263,6 +273,7 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
         return new StringBuilder()
                 .append("{mName=")
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index fbf3056..972b9c0 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -253,8 +253,9 @@
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
      */
-    public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
-            throws ResourceUnavailableException {
+    @NonNull
+    public SecurityParameterIndex allocateSecurityParameterIndex(
+                @NonNull InetAddress destinationAddress) throws ResourceUnavailableException {
         try {
             return new SecurityParameterIndex(
                     mService,
@@ -280,8 +281,9 @@
      * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
      *     reserved
      */
+    @NonNull
     public SecurityParameterIndex allocateSecurityParameterIndex(
-            InetAddress destinationAddress, int requestedSpi)
+            @NonNull InetAddress destinationAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
             throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
@@ -318,9 +320,8 @@
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            Socket socket, @PolicyDirection int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull Socket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -353,9 +354,8 @@
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            DatagramSocket socket, @PolicyDirection int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull DatagramSocket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -388,9 +388,8 @@
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            FileDescriptor socket, @PolicyDirection int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull FileDescriptor socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
         // constructor takes control and closes the user's FD when we exit the method.
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
@@ -413,8 +412,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(Socket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -431,8 +429,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(DatagramSocket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException {
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -449,8 +446,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(FileDescriptor socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
             mService.removeTransportModeTransforms(pfd);
         } catch (RemoteException e) {
@@ -588,6 +584,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
             throws IOException, ResourceUnavailableException {
         /*
@@ -617,6 +614,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket()
             throws IOException, ResourceUnavailableException {
         return new UdpEncapsulationSocket(mService, 0);
@@ -645,6 +643,7 @@
         private int mResourceId = INVALID_RESOURCE_ID;
 
         /** Get the underlying SPI held by this object. */
+        @NonNull
         public String getInterfaceName() {
             return mInterfaceName;
         }
@@ -656,10 +655,16 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
-         * @throws IOException if the address could not be added
          * @hide
          */
-        public void addAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+        public void addAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.addAddressToTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -668,10 +673,16 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
-         * @throws IOException if the address could not be removed
          * @hide
          */
-        public void removeAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+        public void removeAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.removeAddressFromTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         private IpSecTunnelInterface(@NonNull IIpSecService service,
@@ -758,7 +769,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
             throws ResourceUnavailableException, IOException {
@@ -783,9 +795,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
-    public void applyTunnelModeTransform(IpSecTunnelInterface tunnel,
-            @PolicyDirection int direction, IpSecTransform transform) throws IOException {
+    @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+    public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
                     tunnel.getResourceId(), direction, transform.getResourceId());
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 60e96f9..099fe02 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -282,7 +282,7 @@
      */
     @SystemApi
     @RequiresPermission(anyOf = {
-            android.Manifest.permission.NETWORK_STACK,
+            android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
             android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
     })
     public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
@@ -325,7 +325,7 @@
      */
     @SystemApi
     @RequiresPermission(anyOf = {
-            android.Manifest.permission.NETWORK_STACK,
+            android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
             android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
     })
     public void stopNattKeepalive() {
@@ -350,6 +350,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
             Preconditions.checkNotNull(algo);
@@ -364,6 +365,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
             Preconditions.checkNotNull(algo);
@@ -384,6 +386,7 @@
          * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
          *     be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
             Preconditions.checkNotNull(algo);
             mConfig.setAuthenticatedEncryption(algo);
@@ -403,6 +406,7 @@
          * @param remotePort the UDP port number of the remote host that will send and receive
          *     encapsulated traffic. In the case of IKEv2, this should be port 4500.
          */
+        @NonNull
         public IpSecTransform.Builder setIpv4Encapsulation(
                 @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
             Preconditions.checkNotNull(localSocket);
@@ -436,6 +440,7 @@
          *     collides with an existing transform
          * @throws IOException indicating other errors
          */
+        @NonNull
         public IpSecTransform buildTransportModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
@@ -472,7 +477,8 @@
          * @hide
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public IpSecTransform buildTunnelModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index fdcc304..4f92fa6 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -24,6 +24,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
@@ -205,6 +206,19 @@
         }
 
         /**
+         * Set the watched UIDs for this request. This will be reset and wiped out unless
+         * the calling app holds the CHANGE_NETWORK_STATE permission.
+         *
+         * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+         * @return The builder to facilitate chaining.
+         * @hide
+         */
+        public Builder setUids(Set<UidRange> uids) {
+            mNetworkCapabilities.setUids(uids);
+            return this;
+        }
+
+        /**
          * Add a capability that must not exist in the requested network.
          * <p>
          * If the capability was previously added to the list of required capabilities (for
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index ffc735c..66cdc99 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -80,25 +80,27 @@
      *
      * @param host host name of the server.
      * @param timeout network timeout in milliseconds.
+     * @param network network over which to send the request.
      * @return true if the transaction was successful.
      */
-    public boolean requestTime(String host, int timeout) {
+    public boolean requestTime(String host, int timeout, Network network) {
         InetAddress address = null;
         try {
-            address = InetAddress.getByName(host);
+            address = network.getByName(host);
         } catch (Exception e) {
             EventLogTags.writeNtpFailure(host, e.toString());
             if (DBG) Log.d(TAG, "request time failed: " + e);
             return false;
         }
-        return requestTime(address, NTP_PORT, timeout);
+        return requestTime(address, NTP_PORT, timeout, network);
     }
 
-    public boolean requestTime(InetAddress address, int port, int timeout) {
+    public boolean requestTime(InetAddress address, int port, int timeout, Network network) {
         DatagramSocket socket = null;
         final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
         try {
             socket = new DatagramSocket();
+            network.bindSocket(socket);
             socket.setSoTimeout(timeout);
             byte[] buffer = new byte[NTP_PACKET_SIZE];
             DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);
@@ -168,6 +170,12 @@
         return true;
     }
 
+    @Deprecated
+    public boolean requestTime(String host, int timeout) {
+        Log.w(TAG, "Shame on you for calling the hidden API requestTime()!");
+        return false;
+    }
+
     /**
      * Returns the time computed from the NTP transaction.
      *
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b02d48d..c3f23a1 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -225,7 +225,7 @@
      * Indicates the Secure Element on which the transaction occurred.
      * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
      */
-    public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
 
     public static final int STATE_OFF = 1;
     public static final int STATE_TURNING_ON = 2;
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 5312dca..f5a7433 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -357,6 +357,23 @@
     }
 
     /**
+     * Does a loose equality check between two given {@link BaseBundle} objects.
+     * Returns {@code true} if both are {@code null}, or if both are equal as per
+     * {@link #kindofEquals(BaseBundle)}
+     *
+     * @param a A {@link BaseBundle} object
+     * @param b Another {@link BaseBundle} to compare with a
+     * @return {@code true} if both are the same, {@code false} otherwise
+     *
+     * @see #kindofEquals(BaseBundle)
+     *
+     * @hide
+     */
+    public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
+        return (a == b) || (a != null && a.kindofEquals(b));
+    }
+
+    /**
      * @hide This kind-of does an equality comparison.  Kind-of.
      */
     public boolean kindofEquals(BaseBundle other) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7bc5d5b..f528d63 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@
 import android.server.ServerProtoEnums;
 import android.service.batterystats.BatteryStatsServiceDumpProto;
 import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.LongSparseArray;
@@ -2270,27 +2271,8 @@
      */
     public abstract int getMobileRadioActiveUnknownCount(int which);
 
-    public static final int DATA_CONNECTION_NONE     = SystemProto.DataConnection.NONE;      // 0
-    public static final int DATA_CONNECTION_GPRS     = SystemProto.DataConnection.GPRS;      // 1
-    public static final int DATA_CONNECTION_EDGE     = SystemProto.DataConnection.EDGE;      // 2
-    public static final int DATA_CONNECTION_UMTS     = SystemProto.DataConnection.UMTS;      // 3
-    public static final int DATA_CONNECTION_CDMA     = SystemProto.DataConnection.CDMA;      // 4
-    public static final int DATA_CONNECTION_EVDO_0   = SystemProto.DataConnection.EVDO_0;    // 5
-    public static final int DATA_CONNECTION_EVDO_A   = SystemProto.DataConnection.EVDO_A;    // 6
-    public static final int DATA_CONNECTION_1xRTT    = SystemProto.DataConnection.ONE_X_RTT; // 7
-    public static final int DATA_CONNECTION_HSDPA    = SystemProto.DataConnection.HSDPA;     // 8
-    public static final int DATA_CONNECTION_HSUPA    = SystemProto.DataConnection.HSUPA;     // 9
-    public static final int DATA_CONNECTION_HSPA     = SystemProto.DataConnection.HSPA;      // 10
-    public static final int DATA_CONNECTION_IDEN     = SystemProto.DataConnection.IDEN;      // 11
-    public static final int DATA_CONNECTION_EVDO_B   = SystemProto.DataConnection.EVDO_B;    // 12
-    public static final int DATA_CONNECTION_LTE      = SystemProto.DataConnection.LTE;       // 13
-    public static final int DATA_CONNECTION_EHRPD    = SystemProto.DataConnection.EHRPD;     // 14
-    public static final int DATA_CONNECTION_HSPAP    = SystemProto.DataConnection.HSPAP;     // 15
-    public static final int DATA_CONNECTION_GSM      = SystemProto.DataConnection.GSM;       // 16
-    public static final int DATA_CONNECTION_TD_SCDMA = SystemProto.DataConnection.TD_SCDMA;  // 17
-    public static final int DATA_CONNECTION_IWLAN    = SystemProto.DataConnection.IWLAN;     // 18
-    public static final int DATA_CONNECTION_LTE_CA   = SystemProto.DataConnection.LTE_CA;    // 19
-    public static final int DATA_CONNECTION_OTHER    = SystemProto.DataConnection.OTHER;     // 20
+    public static final int DATA_CONNECTION_NONE = 0;
+    public static final int DATA_CONNECTION_OTHER = TelephonyManager.MAX_NETWORK_TYPE + 1;
 
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
@@ -7156,13 +7138,20 @@
 
                 for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
                     final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+
+                    final long startTimeMs = roundUsToMs(ss.getStartTime(batteryUptimeUs, which));
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTimeMs == 0 && starts == 0 && launches == 0) {
+                        continue;
+                    }
+
                     long sToken = proto.start(UidProto.Package.SERVICES);
 
                     proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
-                    proto.write(UidProto.Package.Service.START_DURATION_MS,
-                            roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
-                    proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
-                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+                    proto.write(UidProto.Package.Service.START_DURATION_MS, startTimeMs);
+                    proto.write(UidProto.Package.Service.START_COUNT, starts);
+                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, launches);
 
                     proto.end(sToken);
                 }
@@ -7613,8 +7602,18 @@
 
         // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
         for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
+            // Map OTHER to TelephonyManager.NETWORK_TYPE_UNKNOWN and mark NONE as a boolean.
+            boolean isNone = (i == DATA_CONNECTION_NONE);
+            int telephonyNetworkType = i;
+            if (i == DATA_CONNECTION_OTHER) {
+                telephonyNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
             final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
-            proto.write(SystemProto.DataConnection.NAME, i);
+            if (isNone) {
+                proto.write(SystemProto.DataConnection.IS_NONE, isNone);
+            } else {
+                proto.write(SystemProto.DataConnection.NAME, telephonyNetworkType);
+            }
             dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
                     rawRealtimeUs, which);
             proto.end(pdcToken);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ff7c0c6..0ae5394 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -23,6 +23,7 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 
+import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
@@ -376,7 +377,9 @@
      * Add the calling thread to the IPC thread pool.  This function does
      * not return until the current process is exiting.
      */
-    public static final native void joinThreadPool();
+    public static final void joinThreadPool() {
+        BinderInternal.joinThreadPool();
+    }
 
     /**
      * Returns true if the specified interface is a proxy.
@@ -710,6 +713,8 @@
     // Entry point from android_util_Binder.cpp's onTransact
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
+        BinderCallsStats binderCallsStats = BinderCallsStats.getInstance();
+        BinderCallsStats.CallSession callSession = binderCallsStats.callStarted(this, code);
         Parcel data = Parcel.obtain(dataObj);
         Parcel reply = Parcel.obtain(replyObj);
         // theoretically, we should call transact, which will call onTransact,
@@ -754,6 +759,7 @@
         // to the main transaction loop to wait for another incoming transaction.  Either
         // way, strict mode begone!
         StrictMode.clearGatheredViolations();
+        binderCallsStats.callEnded(callSession);
 
         return res;
     }
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 0417ded..f5bca04 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -220,7 +220,7 @@
      *
      * Asynchronous messages represent interrupts or events that do not require global ordering
      * with respect to synchronous messages.  Asynchronous messages are not subject to
-     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     * the synchronization barriers introduced by conditions such as display vsync.
      *
      * @param looper The looper, must not be null.
      * @param callback The callback interface in which to handle messages, or null.
@@ -236,6 +236,43 @@
         mAsynchronous = async;
     }
 
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper, Callback) to create an async Handler with custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        return new Handler(looper, null, true);
+    }
+
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper) to create an async Handler without custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        if (callback == null) throw new NullPointerException("callback must not be null");
+        return new Handler(looper, callback, true);
+    }
+
     /** @hide */
     @NonNull
     public static Handler getMain() {
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 1681f11..13e4e38 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -65,4 +65,7 @@
 
     // sets the attention light (used by phone app only)
     void setAttentionLight(boolean on, int color);
+
+    // controls whether PowerManager should doze after the screen turns off or not
+    void setDozeAfterScreenOff(boolean on);
 }
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 9b6d6e5..0e6652d 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -21,7 +21,6 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
-import android.provider.Settings;
 import android.util.Slog;
 
 /**
@@ -57,47 +56,6 @@
         reportIncidentInternal(args);
     }
 
-    /**
-     * Convenience method to trigger an incident report and put it in dropbox.
-     * <p>
-     * The fields that are reported will be looked up in the system setting named by
-     * the settingName parameter.  The setting must match one of these patterns:
-     *      The string "disabled": The report will not be taken.
-     *      The string "all": The report will taken with all sections.
-     *      The string "none": The report will taken with no sections, but with the header.
-     *      A comma separated list of field numbers: The report will have these fields.
-     * <p>
-     * The header parameter will be added as a header for the incident report.  Fill in a
-     * {@link android.util.proto.ProtoOutputStream ProtoOutputStream}, and then call the
-     * {@link android.util.proto.ProtoOutputStream#bytes bytes()} method to retrieve
-     * the encoded data for the header.
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.DUMP,
-            android.Manifest.permission.PACKAGE_USAGE_STATS
-    })
-    public void reportIncident(String settingName, byte[] headerProto) {
-        // Sections
-        String setting = Settings.Global.getString(mContext.getContentResolver(), settingName);
-        IncidentReportArgs args;
-        try {
-            args = IncidentReportArgs.parseSetting(setting);
-        } catch (IllegalArgumentException ex) {
-            Slog.w(TAG, "Bad value for incident report setting '" + settingName + "'", ex);
-            return;
-        }
-        if (args == null) {
-            Slog.i(TAG, String.format("Incident report requested but disabled with "
-                    + "settings [name: %s, value: %s]", settingName, setting));
-            return;
-        }
-
-        args.addHeader(headerProto);
-
-        Slog.i(TAG, "Taking incident report: " + settingName);
-        reportIncidentInternal(args);
-    }
-
     private class IncidentdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 9fa129c..1aeac5f 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -188,53 +188,5 @@
     public void addHeader(byte[] header) {
         mHeaders.add(header);
     }
-
-    /**
-     * Parses an incident report config as described in the system setting.
-     *
-     * @see IncidentManager#reportIncident
-     */
-    public static IncidentReportArgs parseSetting(String setting)
-            throws IllegalArgumentException {
-        if (setting == null || setting.length() == 0) {
-            return null;
-        }
-        setting = setting.trim();
-        if (setting.length() == 0 || "disabled".equals(setting)) {
-            return null;
-        }
-
-        final IncidentReportArgs args = new IncidentReportArgs();
-
-        if ("all".equals(setting)) {
-            args.setAll(true);
-            return args;
-        } else if ("none".equals(setting)) {
-            return args;
-        }
-
-        final String[] splits = setting.split(",");
-        final int N = splits.length;
-        for (int i=0; i<N; i++) {
-            final String str = splits[i].trim();
-            if (str.length() == 0) {
-                continue;
-            }
-            int section;
-            try {
-                section = Integer.parseInt(str);
-            } catch (NumberFormatException ex) {
-                throw new IllegalArgumentException("Malformed setting. Bad integer at section"
-                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
-            }
-            if (section < 1) {
-                throw new IllegalArgumentException("Malformed setting. Illegal section at"
-                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
-            }
-            args.addSection(section);
-        }
-
-        return args;
-    }
 }
 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 66fa629..c00100b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1280,6 +1280,19 @@
     }
 
     /**
+     * If true, the doze component is not started until after the screen has been
+     * turned off and the screen off animation has been performed.
+     * @hide
+     */
+    public void setDozeAfterScreenOff(boolean dozeAfterScreenOf) {
+        try {
+            mService.setDozeAfterScreenOff(dozeAfterScreenOf);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the reason the phone was last shutdown. Calling app must have the
      * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
      * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index a9b8675..8eb39c0 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -205,7 +205,12 @@
             }
             ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
             for (int i=0; i<callbacks.size(); i++) {
-                callbacks.get(i).run();
+                try {
+                    callbacks.get(i).run();
+                } catch (Throwable t) {
+                    Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+                    // Ignore and try to go on.
+                }
             }
         }
     }
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 9ac2b3e..3bf09ec 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -20,6 +20,10 @@
 
 import com.google.android.rappor.Encoder;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Random;
 
@@ -66,7 +70,7 @@
             random = sSecureRandom;
         } else {
             // To have deterministic result by hard coding encoder id as seed.
-            random = new Random((long) config.mEncoderId.hashCode());
+            random = new Random(getInsecureSeed(config.mEncoderId));
             userSecret = INSECURE_SECRET;
         }
         mEncoder = new Encoder(random, null, null,
@@ -75,6 +79,17 @@
                 config.mNumCohorts, config.mNumBloomHashes);
     }
 
+    private long getInsecureSeed(String input) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+            return ByteBuffer.wrap(bytes).getLong();
+        } catch (NoSuchAlgorithmException e) {
+            // Should not happen
+            throw new AssertionError("Unable generate insecure seed");
+        }
+    }
+
     /**
      * Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
      *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 70615c8..18fd67e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -83,7 +83,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
-import android.util.StatsLog;
 import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -784,6 +783,21 @@
             "android.settings.APPLICATION_DETAILS_SETTINGS";
 
     /**
+     * Activity Action: Show the "Open by Default" page in a particular application's details page.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+     * <p>
+     * Input: The Intent's data URI specifies the application package name
+     * to be shown, with the "package" scheme. That is "package:com.my.app".
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE =
+            "android.settings.APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE";
+
+    /**
      * Activity Action: Show list of applications that have been running
      * foreground services (to the user "running in the background").
      * <p>
@@ -1715,6 +1729,34 @@
     })
     public @interface ResetMode{}
 
+
+    /**
+     * User has not started setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0;
+
+    /**
+     * User has not yet completed setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
+
+    /**
+     * User has completed setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            USER_SETUP_PERSONALIZATION_NOT_STARTED,
+            USER_SETUP_PERSONALIZATION_STARTED,
+            USER_SETUP_PERSONALIZATION_COMPLETE
+    })
+    public @interface UserSetupPersonalization {}
+
     /**
      * Activity Extra: Number of certificates
      * <p>
@@ -1914,11 +1956,7 @@
                     arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                String prevValue = getStringForUser(cr, name, userHandle);
                 cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
-                String newValue = getStringForUser(cr, name, userHandle);
-                StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
-                        makeDefault, userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                 return false;
@@ -5441,6 +5479,15 @@
         public static final String USER_SETUP_COMPLETE = "user_setup_complete";
 
         /**
+         * The current state of device personalization.
+         *
+         * @hide
+         * @see UserSetupPersonalization
+         */
+        public static final String USER_SETUP_PERSONALIZATION_STATE =
+                "user_setup_personalization_state";
+
+        /**
          * Whether the current user has been set up via setup wizard (0 = false, 1 = true)
          * This value differs from USER_SETUP_COMPLETE in that it can be reset back to 0
          * in case SetupWizard has been re-enabled on TV devices.
@@ -7715,6 +7762,37 @@
         public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
 
         /**
+         * What behavior should be invoked when the volume hush gesture is triggered
+         * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
+         *
+         * @hide
+         */
+        public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
+
+        /** @hide */ public static final int VOLUME_HUSH_OFF = 0;
+        /** @hide */ public static final int VOLUME_HUSH_VIBRATE = 1;
+        /** @hide */ public static final int VOLUME_HUSH_MUTE = 2;
+
+        private static final Validator VOLUME_HUSH_GESTURE_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
+        /**
+         * The number of times (integer) the user has manually enabled battery saver.
+         * @hide
+         */
+        public static final String LOW_POWER_MANUAL_ACTIVATION_COUNT =
+                "low_power_manual_activation_count";
+
+        /**
+         * Whether the "first time battery saver warning" dialog needs to be shown (0: default)
+         * or not (1).
+         *
+         * @hide
+         */
+        public static final String LOW_POWER_WARNING_ACKNOWLEDGED =
+                "low_power_warning_acknowledged";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -7812,6 +7890,7 @@
             SCREENSAVER_ACTIVATE_ON_SLEEP,
             LOCKDOWN_IN_POWER_MENU,
             SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+            VOLUME_HUSH_GESTURE
         };
 
         /**
@@ -7948,6 +8027,7 @@
             VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
             VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                     SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+            VALIDATORS.put(VOLUME_HUSH_GESTURE, VOLUME_HUSH_GESTURE_VALIDATOR);
             VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
                     ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
             VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
@@ -8753,21 +8833,6 @@
             "location_background_throttle_package_whitelist";
 
         /**
-         * The interval in milliseconds at which wifi scan requests will be throttled when they are
-         * coming from the background.
-         * @hide
-         */
-        public static final String WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS =
-                "wifi_scan_background_throttle_interval_ms";
-
-        /**
-         * Packages that are whitelisted to be exempt for wifi background throttling.
-         * @hide
-         */
-        public static final String WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST =
-                "wifi_scan_background_throttle_package_whitelist";
-
-        /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
@@ -9519,6 +9584,12 @@
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
+        /**
+         * The mode that BLE scanning clients will be moved to when in the background.
+         * @hide
+         */
+        public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+
        /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
@@ -9596,6 +9667,21 @@
         public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
                 "wifi_connected_mac_randomization_enabled";
 
+        /**
+         * Parameters to adjust the performance of framework wifi scoring methods.
+         * <p>
+         * Encoded as a comma-separated key=value list, for example:
+         *   "rssi5=-80:-77:-70:-57,rssi2=-83:-80:-73:-60,horizon=15"
+         * This is intended for experimenting with new parameter values,
+         * and is normally unset or empty. The example does not include all
+         * parameters that may be honored.
+         * Default values are provided by code or device configurations.
+         * Errors in the parameters will cause the entire setting to be ignored.
+         * @hide
+         */
+        public static final String WIFI_SCORE_PARAMS =
+                "wifi_score_params";
+
        /**
         * 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.
@@ -11556,8 +11642,8 @@
          */
         @SystemApi
         @TestApi
-        public static final String AUTOFILL_COMPAT_ALLOWED_PACKAGES =
-                "autofill_compat_allowed_packages";
+        public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
+                "autofill_compat_mode_allowed_packages";
 
         /**
          * Exemptions to the hidden API blacklist.
@@ -11569,6 +11655,24 @@
                 "hidden_api_blacklist_exemptions";
 
         /**
+         * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operation (in ms).
+         *
+         * @hide
+         */
+        public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
+                "sound_trigger_detection_service_op_timeout";
+
+        /**
+         * Maximum number of {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operations per day.
+         *
+         * @hide
+         */
+        public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
+                "max_sound_trigger_detection_service_ops_per_day";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
@@ -12304,6 +12408,16 @@
                 "zram_enabled";
 
         /**
+         * Whether we have enable CPU frequency scaling for this device.
+         * For Wear, default is disable.
+         *
+         * The value is "1" for enable, "0" for disable.
+         * @hide
+         */
+        public static final String CPU_SCALING_ENABLED =
+                "cpu_frequency_scaling_enabled";
+
+        /**
          * Configuration flags for smart replies in notifications.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -12368,6 +12482,16 @@
          */
         public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
                 "backup_agent_timeout_parameters";
+
+        /**
+         * Whether we have enabled swapping on this device. For Wear, default is
+         * enabled.
+         *
+         * The value is "1" for enable, "0" for disable.
+         * @hide
+         */
+         public static final String SWAP_ENABLED = "swap_enabled";
+
     }
 
     /**
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index 3d3b6d5..d42424e 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -52,7 +52,7 @@
 public final class KeyChainProtectionParams implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+    @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
     public @interface UserSecretType {
     }
 
@@ -61,11 +61,6 @@
      */
     public static final int TYPE_LOCKSCREEN = 100;
 
-    /**
-     * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
-     */
-    public static final int TYPE_CUSTOM_PASSWORD = 101;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"UI_FORMAT_"}, value = {UI_FORMAT_PIN, UI_FORMAT_PASSWORD, UI_FORMAT_PATTERN})
@@ -120,7 +115,6 @@
 
     /**
      * @see TYPE_LOCKSCREEN
-     * @see TYPE_CUSTOM_PASSWORD
      */
     public @UserSecretType int getUserSecretType() {
         return mUserSecretType;
@@ -166,7 +160,6 @@
          * Sets user secret type.
          *
          * @see TYPE_LOCKSCREEN
-         * @see TYPE_CUSTOM_PASSWORD
          * @param userSecretType The secret type
          * @return This builder.
          */
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 00f54e1..69b9123 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -127,18 +127,13 @@
     /**
      * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
      */
-    // TODO: Change to @NonNull
-    public CertPath getTrustedHardwareCertPath() {
-        if (mCertPath == null) {
-            return null;
-        } else {
-            try {
-                return mCertPath.getCertPath();
-            } catch (CertificateException e) {
-                // Rethrow an unchecked exception as it should not happen. If such an issue exists,
-                // an exception should have been thrown during service initialization.
-                throw new BadParcelableException(e);
-            }
+    public @NonNull CertPath getTrustedHardwareCertPath() {
+        try {
+            return mCertPath.getCertPath();
+        } catch (CertificateException e) {
+            // Rethrow an unchecked exception as it should not happen. If such an issue exists,
+            // an exception should have been thrown during service initialization.
+            throw new BadParcelableException(e);
         }
     }
 
@@ -248,13 +243,9 @@
          * @throws CertificateException if the given certificate path cannot be encoded properly
          * @return This builder.
          */
-        public Builder setTrustedHardwareCertPath(CertPath certPath) throws CertificateException {
-            // TODO: Make it NonNull when the caller code is all updated
-            if (certPath == null) {
-                mInstance.mCertPath = null;
-            } else {
-                mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
-            }
+        public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
+                throws CertificateException {
+            mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
             return this;
         }
 
@@ -282,7 +273,7 @@
         }
 
         /**
-         * Sets recovery key blob
+         * Sets recovery key blob.
          *
          * @param encryptedRecoveryKeyBlob The recovery key blob.
          * @return This builder.
@@ -297,7 +288,7 @@
          * Creates a new {@link KeyChainSnapshot} instance.
          *
          * @return new instance
-         * @throws NullPointerException if some required fields were not set.
+         * @throws NullPointerException if some of the required fields were not set.
          */
         @NonNull public KeyChainSnapshot build() {
             Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
@@ -306,6 +297,7 @@
                     "entryRecoveryData");
             Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
             Preconditions.checkNotNull(mInstance.mServerParams);
+            Preconditions.checkNotNull(mInstance.mCertPath);
             return mInstance;
         }
     }
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index ef5e90c..8cb8e51 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -37,40 +37,66 @@
 @SystemApi
 public final class KeyDerivationParams implements Parcelable {
     private final int mAlgorithm;
-    private byte[] mSalt;
+    private final byte[] mSalt;
+    private final int mMemoryDifficulty;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_SCRYPT})
     public @interface KeyDerivationAlgorithm {
     }
 
     /**
-     * Salted SHA256
+     * Salted SHA256.
      */
     public static final int ALGORITHM_SHA256 = 1;
 
     /**
-     * Argon2ID
-     * @hide
+     * SCRYPT.
      */
-    // TODO: add Argon2ID support.
-    public static final int ALGORITHM_ARGON2ID = 2;
+    public static final int ALGORITHM_SCRYPT = 2;
 
     /**
-     * Creates instance of the class to to derive key using salted SHA256 hash.
+     * Creates instance of the class to to derive keys using salted SHA256 hash.
+     *
+     * <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
+     * salt + key_material_len + key_material, where salt_len and key_material_len are one-byte, and
+     * denote the number of bytes for salt and key_material, respectively.
      */
     public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
         return new KeyDerivationParams(ALGORITHM_SHA256, salt);
     }
 
     /**
+     * Creates instance of the class to to derive keys using the password hashing algorithm SCRYPT.
+     *
+     * <p>We expose only one tuning parameter of SCRYPT, which is the memory cost parameter (i.e. N
+     * in <a href="https://www.tarsnap.com/scrypt/scrypt.pdf">the SCRYPT paper</a>). Regular/default
+     * values are used for the other parameters, to keep the overall running time low. Specifically,
+     * the parallelization parameter p is 1, the block size parameter r is 8, and the hashing output
+     * length is 32-byte.
+     */
+    public static KeyDerivationParams createScryptParams(
+            @NonNull byte[] salt, int memoryDifficulty) {
+        return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, memoryDifficulty);
+    }
+
+    /**
      * @hide
      */
     // TODO: Make private once legacy API is removed
     public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+        this(algorithm, salt, /*memoryDifficulty=*/ -1);
+    }
+
+    /**
+     * @hide
+     */
+    KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
+            int memoryDifficulty) {
         mAlgorithm = algorithm;
         mSalt = Preconditions.checkNotNull(salt);
+        mMemoryDifficulty = memoryDifficulty;
     }
 
     /**
@@ -87,6 +113,19 @@
         return mSalt;
     }
 
+    /**
+     * Gets the memory difficulty parameter for the hashing algorithm.
+     *
+     * <p>The effect of this parameter depends on the algorithm in use. For example, please see
+     * {@link #createScryptParams(byte[], int)} for choosing the parameter for SCRYPT.
+     *
+     * <p>If the specific algorithm does not support such a memory difficulty parameter, its value
+     * should be -1.
+     */
+    public int getMemoryDifficulty() {
+        return mMemoryDifficulty;
+    }
+
     public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
             new Parcelable.Creator<KeyDerivationParams>() {
         public KeyDerivationParams createFromParcel(Parcel in) {
@@ -102,6 +141,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mAlgorithm);
         out.writeByteArray(mSalt);
+        out.writeInt(mMemoryDifficulty);
     }
 
     /**
@@ -110,6 +150,7 @@
     protected KeyDerivationParams(Parcel in) {
         mAlgorithm = in.readInt();
         mSalt = in.createByteArray();
+        mMemoryDifficulty = in.readInt();
     }
 
     @Override
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 7523afd..aa9d1c0 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -35,6 +35,7 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -265,8 +266,8 @@
 
     /**
      * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
-     * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
-     * most one registered listener at any time.
+     * #getKeyChainSnapshot} can be used to get the snapshot. Note that every recovery agent can
+     * have at most one registered listener at any time.
      *
      * @param intent triggered when new snapshot is available. Unregisters listener if the value is
      *     {@code null}.
@@ -291,12 +292,13 @@
      * in vaultParams {@link RecoverySession#start(CertPath, byte[], byte[], List)}.
      *
      * @param serverParams included in recovery key blob.
-     * @see #getRecoveryData
+     * @see #getKeyChainSnapshot
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+    public void setServerParams(@NonNull byte[] serverParams)
+            throws InternalRecoveryServiceException {
         try {
             mBinder.setServerParams(serverParams);
         } catch (RemoteException e) {
@@ -320,7 +322,7 @@
      * Returns a list of aliases of keys belonging to the application.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public List<String> getAliases() throws InternalRecoveryServiceException {
+    public @NonNull List<String> getAliases() throws InternalRecoveryServiceException {
         try {
             Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             return new ArrayList<>(allStatuses.keySet());
@@ -354,7 +356,7 @@
      *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void setRecoveryStatus(String alias, int status)
+    public void setRecoveryStatus(@NonNull String alias, int status)
             throws InternalRecoveryServiceException {
         try {
             mBinder.setRecoveryStatus(alias, status);
@@ -389,7 +391,7 @@
      *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+    public int getRecoveryStatus(@NonNull String alias) throws InternalRecoveryServiceException {
         try {
             Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             Integer status = allStatuses.get(alias);
@@ -409,8 +411,7 @@
      * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
      * is necessary to recover data.
      *
-     * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} or {@link
-     *     KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}
+     * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN}
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
@@ -449,51 +450,6 @@
     }
 
     /**
-     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
-     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
-     * called.
-     *
-     * @return list of recovery secret types
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    @NonNull
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
-            throws InternalRecoveryServiceException {
-        try {
-            return mBinder.getPendingRecoverySecretTypes();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Method notifies KeyStore that a user-generated secret is available. This method generates a
-     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
-     * should use {@link KeyChainProtectionParams#clearSecret} to override the secret value in
-     * memory.
-     *
-     * @param recoverySecret user generated secret together with parameters necessary to regenerate
-     *     it on a new device.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
-            throws InternalRecoveryServiceException {
-        try {
-            mBinder.recoverySecretAvailable(recoverySecret);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
      * Deprecated.
      * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
      * key store. Returns the raw material of the key.
@@ -547,10 +503,7 @@
             if (grantAlias == null) {
                 throw new InternalRecoveryServiceException("null grant alias");
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (UnrecoverableKeyException e) {
@@ -581,10 +534,7 @@
             if (grantAlias == null) {
                 throw new InternalRecoveryServiceException("Null grant alias");
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (UnrecoverableKeyException e) {
@@ -614,10 +564,7 @@
             if (grantAlias == null) {
                 return null;
             }
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
-                    mKeyStore,
-                    grantAlias,
-                    KeyStore.UID_SELF);
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -626,6 +573,16 @@
     }
 
     /**
+     * Returns the key with the given {@code grantAlias}.
+     */
+    Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException {
+        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+                mKeyStore,
+                grantAlias,
+                KeyStore.UID_SELF);
+    }
+
+    /**
      * Removes a key called {@code alias} from the recoverable key store.
      *
      * @param alias The key alias.
@@ -649,10 +606,15 @@
      * <p>A recovery session is required to restore keys from a remote store.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public RecoverySession createRecoverySession() {
+    public @NonNull RecoverySession createRecoverySession() {
         return RecoverySession.newInstance(this);
     }
 
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public Map<String, X509Certificate> getRootCertificates() {
+        return TrustedRootCertificates.getRootCertificates();
+    }
+
     InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
             ServiceSpecificException e) {
         if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 137dd89..cf8a9dd 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -16,17 +16,22 @@
 
 package android.security.keystore.recovery;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import java.security.Key;
 import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -44,7 +49,8 @@
     private final String mSessionId;
     private final RecoveryController mRecoveryController;
 
-    private RecoverySession(RecoveryController recoveryController, String sessionId) {
+    private RecoverySession(@NonNull RecoveryController recoveryController,
+            @NonNull String sessionId) {
         mRecoveryController = recoveryController;
         mSessionId = sessionId;
     }
@@ -53,14 +59,14 @@
      * A new session, started by the {@link RecoveryController}.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    static RecoverySession newInstance(RecoveryController recoveryController) {
+    static @NonNull RecoverySession newInstance(RecoveryController recoveryController) {
         return new RecoverySession(recoveryController, newSessionId());
     }
 
     /**
      * Returns a new random session ID.
      */
-    private static String newSessionId() {
+    private static @NonNull String newSessionId() {
         SecureRandom secureRandom = new SecureRandom();
         byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
         secureRandom.nextBytes(sessionId);
@@ -72,7 +78,7 @@
     }
 
     /**
-     * @deprecated Use {@link #start(CertPath, byte[], byte[], List)} instead.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -103,25 +109,9 @@
     }
 
     /**
-     * Starts a recovery session and returns a blob with proof of recovery secret possession.
-     * The method generates a symmetric key for a session, which trusted remote device can use to
-     * return recovery key.
-     *
-     * @param verifierCertPath The certificate path used to create the recovery blob on the source
-     *     device. Keystore will verify the certificate path by using the root of trust.
-     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
-     *     Used to limit number of guesses.
-     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
-     *     replay attacks.
-     * @param secrets Secrets provided by user, the method only uses type and secret fields.
-     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
-     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
-     *     key and parameters necessary to identify the counter with the number of failed recovery
-     *     attempts.
-     * @throws CertificateException if the {@code verifierCertPath} is invalid.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     @NonNull public byte[] start(
             @NonNull CertPath verifierCertPath,
@@ -136,6 +126,7 @@
             byte[] recoveryClaim =
                     mRecoveryController.getBinder().startRecoverySessionWithCertPath(
                             mSessionId,
+                            /*rootCertificateAlias=*/ "",  // Use the default root cert
                             recoveryCertPath,
                             vaultParams,
                             vaultChallenge,
@@ -153,18 +144,64 @@
     }
 
     /**
-     * Imports keys.
+     * Starts a recovery session and returns a blob with proof of recovery secret possession.
+     * The method generates a symmetric key for a session, which trusted remote device can use to
+     * return recovery key.
      *
-     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
-     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
-     *     and session. KeyStore only uses package names from the application info in {@link
-     *     WrappedApplicationKey}. Caller is responsibility to perform certificates check.
-     * @return Map from alias to raw key material.
-     * @throws SessionExpiredException if {@code session} has since been closed.
-     * @throws DecryptionFailedException if unable to decrypt the snapshot.
-     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
+     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
+     * @param verifierCertPath The certificate path used to create the recovery blob on the source
+     *     device. Keystore will verify the certificate path by using the root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     *     Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     *     replay attacks.
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+     *     key and parameters necessary to identify the counter with the number of failed recovery
+     *     attempts.
+     * @throws CertificateException if the {@code verifierCertPath} is invalid.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public byte[] start(
+            @NonNull String rootCertificateAlias,
+            @NonNull CertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+        RecoveryCertPath recoveryCertPath =
+                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+                            mSessionId,
+                            rootCertificateAlias,
+                            recoveryCertPath,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public Map<String, byte[]> recoverKeys(
             @NonNull byte[] recoveryKeyBlob,
             @NonNull List<WrappedApplicationKey> applicationKeys)
@@ -187,6 +224,61 @@
     }
 
     /**
+     * Imports key chain snapshot recovered from a remote vault.
+     *
+     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+     *     and session.
+     * @throws SessionExpiredException if {@code session} has since been closed.
+     * @throws DecryptionFailedException if unable to decrypt the snapshot.
+     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     */
+    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
+    public Map<String, Key> recoverKeyChainSnapshot(
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys
+    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
+        try {
+            Map<String, String> grantAliases = mRecoveryController
+                    .getBinder()
+                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
+            return getKeysFromGrants(grantAliases);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
+                throw new DecryptionFailedException(e.getMessage());
+            }
+            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
+                throw new SessionExpiredException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
+    private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases)
+            throws InternalRecoveryServiceException {
+        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
+        for (String alias : grantAliases.keySet()) {
+            String grantAlias = grantAliases.get(alias);
+            Key key;
+            try {
+                key = mRecoveryController.getKeyFromGrant(grantAlias);
+            } catch (UnrecoverableKeyException e) {
+                throw new InternalRecoveryServiceException(
+                        String.format(
+                                Locale.US,
+                                "Failed to get key '%s' from grant '%s'",
+                                alias,
+                                grantAlias), e);
+            }
+            keysByAlias.put(alias, key);
+        }
+        return keysByAlias;
+    }
+
+    /**
      * An internal session ID, used by the framework to match recovery claims to snapshot responses.
      *
      * @hide
diff --git a/core/java/android/security/keystore/recovery/TrustedRootCertificates.java b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
new file mode 100644
index 0000000..a65b40f
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
@@ -0,0 +1,120 @@
+/*
+ * 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.security.keystore.recovery;
+
+import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert;
+
+import android.util.ArrayMap;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * Trusted root certificates for use by the
+ * {@link android.security.keystore.recovery.RecoveryController}. These certificates are used to
+ * verify the public keys of remote secure hardware modules. This is to prevent AOSP backing up keys
+ * to untrusted devices.
+ *
+ * @hide
+ */
+public final class TrustedRootCertificates {
+
+    public static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS =
+            "GoogleCloudKeyVaultServiceV1";
+
+    private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64 = ""
+            + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
+            + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
+            + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
+            + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P"
+            + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2"
+            + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w"
+            + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA"
+            + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq"
+            + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU"
+            + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT"
+            + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca"
+            + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ"
+            + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE"
+            + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib"
+            + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc"
+            + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E"
+            + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW"
+            + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7"
+            + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX"
+            + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6"
+            + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl"
+            + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ"
+            + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP"
+            + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z"
+            + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw"
+            + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3"
+            + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
+            + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
+
+    /**
+     * The X509 certificate of the trusted root CA cert for the recoverable key store service.
+     *
+     * TODO: Change it to the production certificate root CA before the final launch.
+     */
+    private static final X509Certificate GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE =
+            parseGoogleCloudKeyVaultServiceV1Certificate();
+
+    private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;
+
+    private static final ArrayMap<String, X509Certificate> ALL_ROOT_CERTIFICATES =
+            constructRootCertificateMap();
+
+    /**
+     * Returns all available root certificates, keyed by alias.
+     */
+    public static Map<String, X509Certificate> getRootCertificates() {
+        return new ArrayMap(ALL_ROOT_CERTIFICATES);
+    }
+
+    /**
+     * Gets a root certificate referenced by the given {@code alias}.
+     *
+     * @param alias the alias of the certificate
+     * @return the certificate referenced by the alias, or null if such a certificate doesn't exist.
+     */
+    public static X509Certificate getRootCertificate(String alias) {
+        return ALL_ROOT_CERTIFICATES.get(alias);
+    }
+
+    private static ArrayMap<String, X509Certificate> constructRootCertificateMap() {
+        ArrayMap<String, X509Certificate> certificates =
+                new ArrayMap<>(NUMBER_OF_ROOT_CERTIFICATES);
+        certificates.put(
+                GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS,
+                GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE);
+        return certificates;
+    }
+
+    private static X509Certificate parseGoogleCloudKeyVaultServiceV1Certificate() {
+        try {
+            return decodeBase64Cert(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
+        } catch (CertificateException e) {
+            // Should not happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    // Statics only
+    private TrustedRootCertificates() {}
+}
diff --git a/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
new file mode 100644
index 0000000..fa72c83
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.security.keystore.recovery;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+
+/**
+ * Static helper methods for decoding {@link X509Certificate} instances.
+ *
+ * @hide
+ */
+public class X509CertificateParsingUtils {
+    private static final String CERT_FORMAT = "X.509";
+
+    /**
+     * Decodes an {@link X509Certificate} encoded as a base-64 string.
+     */
+    public static X509Certificate decodeBase64Cert(String string) throws CertificateException {
+        try {
+            return decodeCert(decodeBase64(string));
+        } catch (IllegalArgumentException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    /**
+     * Decodes a base-64 string.
+     *
+     * @throws IllegalArgumentException if not a valid base-64 string.
+     */
+    private static byte[] decodeBase64(String string) {
+        return Base64.getDecoder().decode(string);
+    }
+
+    /**
+     * Decodes a byte array containing an encoded X509 certificate.
+     *
+     * @param certBytes the byte array containing the encoded X509 certificate
+     * @return the decoded X509 certificate
+     * @throws CertificateException if any parsing error occurs
+     */
+    private static X509Certificate decodeCert(byte[] certBytes) throws CertificateException {
+        return decodeCert(new ByteArrayInputStream(certBytes));
+    }
+
+    /**
+     * Decodes an X509 certificate from an {@code InputStream}.
+     *
+     * @param inStream the input stream containing the encoded X509 certificate
+     * @return the decoded X509 certificate
+     * @throws CertificateException if any parsing error occurs
+     */
+    private static X509Certificate decodeCert(InputStream inStream) throws CertificateException {
+        CertificateFactory certFactory;
+        try {
+            certFactory = CertificateFactory.getInstance(CERT_FORMAT);
+        } catch (CertificateException e) {
+            // Should not happen, as X.509 is mandatory for all providers.
+            throw new RuntimeException(e);
+        }
+        return (X509Certificate) certFactory.generateCertificate(inStream);
+    }
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 0c5d8bd..60537a4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -518,7 +518,7 @@
  *     &lt;intent-filter&gt;
  *         &lt;action android:name="android.service.autofill.AutofillService" /&gt;
  *     &lt;/intent-filter&gt;
- *     &lt;meta-data android:name="android.autofillservice" android:resource="@xml/autofillservice" /&gt;
+ *     &lt;meta-data android:name="android.autofill" android:resource="@xml/autofillservice" /&gt;
  * &lt;/service&gt;</pre>
  *
  * <P>In the XML file you can specify one or more packages for which to enable compatibility
@@ -527,8 +527,10 @@
  * <pre> &lt;autofill-service xmlns:android="http://schemas.android.com/apk/res/android"&gt;
  *     &lt;compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/&gt;
  * &lt;/autofill-service&gt;</pre>
+ *
+ * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
+ * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
  */
-// TODO(b/70407264): add code snippets to field classification ???
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
 
diff --git a/core/java/android/service/autofill/AutofillServiceHelper.java b/core/java/android/service/autofill/AutofillServiceHelper.java
new file mode 100644
index 0000000..bbaebff
--- /dev/null
+++ b/core/java/android/service/autofill/AutofillServiceHelper.java
@@ -0,0 +1,35 @@
+/*
+ * 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.autofill;
+
+import android.annotation.Nullable;
+import android.view.autofill.AutofillId;
+
+import com.android.internal.util.Preconditions;
+
+/** @hide */
+final class AutofillServiceHelper {
+
+    static AutofillId[] assertValid(@Nullable AutofillId[] ids) {
+        Preconditions.checkArgument(ids != null && ids.length > 0, "must have at least one id");
+        return Preconditions.checkArrayElementsNotNull(ids, "ids");
+    }
+
+    private AutofillServiceHelper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index de23455..b7ec281 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -198,14 +198,6 @@
                     } else {
                         maxVersionCode = Long.MAX_VALUE;
                     }
-                    if (true) { // TODO(b/74445943): remove block after P DP2 is branched
-                        final String urlBarResourceId = cpAttributes.getString(
-                                R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId);
-                        if (urlBarResourceId != null) {
-                            Log.e(TAG, "Service is using deprecated attribute 'urlBarResourceId'");
-                        }
-                    }
-
                     if (compatibilityPackages == null) {
                         compatibilityPackages = new ArrayMap<>();
                     }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index f32dee1..ccec483 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -149,24 +149,33 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("Dataset[id=");
+        final StringBuilder builder = new StringBuilder("Dataset[");
         if (mId == null) {
-            builder.append("null");
+            builder.append("noId");
         } else {
             // Cannot disclose id because it could contain PII.
-            builder.append(mId.length()).append("_chars");
+            builder.append("id=").append(mId.length()).append("_chars");
         }
+        if (mFieldIds != null) {
+            builder.append(", fieldIds=").append(mFieldIds);
+        }
+        if (mFieldValues != null) {
+            builder.append(", fieldValues=").append(mFieldValues);
+        }
+        if (mFieldPresentations != null) {
+            builder.append(", fieldPresentations=").append(mFieldPresentations.size());
 
-        return builder
-                .append(", fieldIds=").append(mFieldIds)
-                .append(", fieldValues=").append(mFieldValues)
-                .append(", fieldPresentations=")
-                .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
-                .append(", fieldFilters=")
-                .append(mFieldFilters == null ? 0 : mFieldFilters.size())
-                .append(", hasPresentation=").append(mPresentation != null)
-                .append(", hasAuthentication=").append(mAuthentication != null)
-                .append(']').toString();
+        }
+        if (mFieldFilters != null) {
+            builder.append(", fieldFilters=").append(mFieldFilters.size());
+        }
+        if (mPresentation != null) {
+            builder.append(", hasPresentation");
+        }
+        if (mAuthentication != null) {
+            builder.append(", hasAuthentication");
+        }
+        return builder.append(']').toString();
     }
 
     /**
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 3a4b6bb..2bc4b8f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import static android.service.autofill.AutofillServiceHelper.assertValid;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.view.autofill.Helper.sDebug;
 
@@ -245,10 +246,15 @@
          * @param ids id of Views that when focused will display the authentication UI.
          *
          * @return This builder.
-
-         * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
-         * both {@code authentication} and {@code presentation} are {@code null}, or if
-         * both {@code authentication} and {@code presentation} are non-{@code null}
+         *
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ul>
+         *   <li>{@code ids} is {@code null}</li>
+         *   <li>{@code ids} is empty</li>
+         *   <li>{@code ids} contains a {@code null} element</li>
+         *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
+         *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
+         * </ul>
          *
          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
@@ -263,16 +269,13 @@
                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
             }
 
-            if (ids == null || ids.length == 0) {
-                throw new IllegalArgumentException("ids cannot be null or empry");
-            }
             if (authentication == null ^ presentation == null) {
                 throw new IllegalArgumentException("authentication and presentation"
                         + " must be both non-null or null");
             }
             mAuthentication = authentication;
             mPresentation = presentation;
-            mAuthenticationIds = ids;
+            mAuthenticationIds = assertValid(ids);
             return this;
         }
 
@@ -554,23 +557,40 @@
         if (!sDebug) return super.toString();
 
         // TODO: create a dump() method instead
-        return new StringBuilder(
-                "FillResponse : [mRequestId=" + mRequestId)
-                .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
-                .append(", saveInfo=").append(mSaveInfo)
-                .append(", clientState=").append(mClientState != null)
-                .append(", hasPresentation=").append(mPresentation != null)
-                .append(", hasHeader=").append(mHeader != null)
-                .append(", hasFooter=").append(mFooter != null)
-                .append(", hasAuthentication=").append(mAuthentication != null)
-                .append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
-                .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
-                .append(", disableDuration=").append(mDisableDuration)
-                .append(", flags=").append(mFlags)
-                .append(", fieldClassificationIds=")
-                    .append(Arrays.toString(mFieldClassificationIds))
-                .append("]")
-                .toString();
+        final StringBuilder builder = new StringBuilder(
+                "FillResponse : [mRequestId=" + mRequestId);
+        if (mDatasets != null) {
+            builder.append(", datasets=").append(mDatasets.getList());
+        }
+        if (mSaveInfo != null) {
+            builder.append(", saveInfo=").append(mSaveInfo);
+        }
+        if (mClientState != null) {
+            builder.append(", hasClientState");
+        }
+        if (mPresentation != null) {
+            builder.append(", hasPresentation");
+        }
+        if (mHeader != null) {
+            builder.append(", hasHeader");
+        }
+        if (mFooter != null) {
+            builder.append(", hasFooter");
+        }
+        if (mAuthentication != null) {
+            builder.append(", hasAuthentication");
+        }
+        if (mAuthenticationIds != null) {
+            builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
+        }
+        builder.append(", disableDuration=").append(mDisableDuration);
+        if (mFlags != 0) {
+            builder.append(", flags=").append(mFlags);
+        }
+        if (mFieldClassificationIds != null) {
+            builder.append(Arrays.toString(mFieldClassificationIds));
+        }
+        return builder.append("]").toString();
     }
 
     /////////////////////////////////////
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index a5a6177..4943fc8 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import static android.service.autofill.AutofillServiceHelper.assertValid;
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.IntDef;
@@ -405,17 +406,6 @@
             mRequiredIds = null;
         }
 
-        private AutofillId[] assertValid(AutofillId[] ids) {
-            Preconditions.checkArgument(ids != null && ids.length > 0,
-                    "must have at least one id: " + Arrays.toString(ids));
-            for (int i = 0; i < ids.length; i++) {
-                final AutofillId id = ids[i];
-                Preconditions.checkArgument(id != null,
-                        "cannot have null id: " + Arrays.toString(ids));
-            }
-            return ids;
-        }
-
         /**
          * Sets flags changing the save behavior.
          *
@@ -699,22 +689,37 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        return new StringBuilder("SaveInfo: [type=")
+        final StringBuilder builder = new StringBuilder("SaveInfo: [type=")
                 .append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
                 .append(", requiredIds=").append(Arrays.toString(mRequiredIds))
-                .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
-                .append(", description=").append(mDescription)
-                .append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
-                        mNegativeButtonStyle))
-                .append(", flags=").append(mFlags)
-                .append(", customDescription=").append(mCustomDescription)
-                .append(", validator=").append(mValidator)
-                .append(", sanitizerKeys=")
-                    .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
-                .append(", sanitizerValues=")
-                    .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
-                .append(", triggerId=").append(mTriggerId)
-                .append("]").toString();
+                .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class,
+                        "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle));
+        if (mOptionalIds != null) {
+            builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds));
+        }
+        if (mDescription != null) {
+            builder.append(", description=").append(mDescription);
+        }
+        if (mFlags != 0) {
+            builder.append(", flags=").append(mFlags);
+        }
+        if (mCustomDescription != null) {
+            builder.append(", customDescription=").append(mCustomDescription);
+        }
+        if (mValidator != null) {
+            builder.append(", validator=").append(mValidator);
+        }
+        if (mSanitizerKeys != null) {
+            builder.append(", sanitizerKeys=").append(mSanitizerKeys.length);
+        }
+        if (mSanitizerValues != null) {
+            builder.append(", sanitizerValues=").append(mSanitizerValues.length);
+        }
+        if (mTriggerId != null) {
+            builder.append(", triggerId=").append(mTriggerId);
+        }
+
+        return builder.append("]").toString();
     }
 
     /////////////////////////////////////
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index a1dd1f8..55aecdd 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -169,17 +169,15 @@
          * @param categoryId string used to identify the category the value is associated with.
          *
          * @throws IllegalArgumentException if any of the following occurs:
-         * <ol>
+         * <ul>
          *   <li>{@code id} is empty</li>
          *   <li>{@code categoryId} is empty</li>
          *   <li>{@code value} is empty</li>
          *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
          *   <li>the length of {@code value} is higher than
          *       {@link UserData#getMaxValueLength()}</li>
-         * </ol>
-         *
+         * </ul>
          */
-        // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
         public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
             mId = checkNotEmpty("id", id);
             checkNotEmpty("categoryId", categoryId);
@@ -222,26 +220,25 @@
          * @param categoryId string used to identify the category the value is associated with.
          *
          * @throws IllegalStateException if:
-         * <ol>
+         * <ul>
          *   <li>{@link #build()} already called</li>
          *   <li>the {@code value} has already been added</li>
          *   <li>the number of unique {@code categoryId} values added so far is more than
          *       {@link UserData#getMaxCategoryCount()}</li>
          *   <li>the number of {@code values} added so far is is more than
          *       {@link UserData#getMaxUserDataSize()}</li>
-         * </ol>
+         * </ul>
          *
          * @throws IllegalArgumentException if any of the following occurs:
-         * <ol>
+         * <ul>
          *   <li>{@code id} is empty</li>
          *   <li>{@code categoryId} is empty</li>
          *   <li>{@code value} is empty</li>
          *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
          *   <li>the length of {@code value} is higher than
          *       {@link UserData#getMaxValueLength()}</li>
-         * </ol>
+         * </ul>
          */
-        // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
         public Builder add(@NonNull String value, @NonNull String categoryId) {
             throwIfDestroyed();
             checkNotEmpty("categoryId", categoryId);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index ea10ae7..3726e66 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1418,6 +1418,7 @@
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
         private boolean mShowBadge;
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
+        private boolean mHidden;
 
         public Ranking() {}
 
@@ -1557,6 +1558,16 @@
         }
 
         /**
+         * Returns whether the app that posted this notification is suspended, so this notification
+         * should be hidden.
+         *
+         * @return true if the notification should be hidden, false otherwise.
+         */
+        public boolean isSuspended() {
+            return mHidden;
+        }
+
+        /**
          * @hide
          */
         @VisibleForTesting
@@ -1565,7 +1576,7 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment) {
+                int userSentiment, boolean hidden) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1580,6 +1591,7 @@
             mSnoozeCriteria = snoozeCriteria;
             mShowBadge = showBadge;
             mUserSentiment = userSentiment;
+            mHidden = hidden;
         }
 
         /**
@@ -1628,6 +1640,7 @@
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
         private ArrayMap<String, Boolean> mShowBadge;
         private ArrayMap<String, Integer> mUserSentiment;
+        private ArrayMap<String, Boolean> mHidden;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1656,7 +1669,7 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key), getUserSentiment(key));
+                    getShowBadge(key), getUserSentiment(key), getHidden(key));
             return rank >= 0;
         }
 
@@ -1784,6 +1797,16 @@
                     ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
         }
 
+        private boolean getHidden(String key) {
+            synchronized (this) {
+                if (mHidden == null) {
+                    buildHiddenLocked();
+                }
+            }
+            Boolean hidden = mHidden.get(key);
+            return hidden == null ? false : hidden.booleanValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1892,6 +1915,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildHiddenLocked() {
+            Bundle hidden = mRankingUpdate.getHidden();
+            mHidden = new ArrayMap<>(hidden.size());
+            for (String key : hidden.keySet()) {
+                mHidden.put(key, hidden.getBoolean(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 6d51db0..00c47ec 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -36,12 +36,13 @@
     private final Bundle mSnoozeCriteria;
     private final Bundle mShowBadge;
     private final Bundle mUserSentiment;
+    private final Bundle mHidden;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge, Bundle userSentiment) {
+            Bundle showBadge, Bundle userSentiment, Bundle hidden) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -54,6 +55,7 @@
         mSnoozeCriteria = snoozeCriteria;
         mShowBadge = showBadge;
         mUserSentiment = userSentiment;
+        mHidden = hidden;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -70,6 +72,7 @@
         mSnoozeCriteria = in.readBundle();
         mShowBadge = in.readBundle();
         mUserSentiment = in.readBundle();
+        mHidden = in.readBundle();
     }
 
     @Override
@@ -91,6 +94,7 @@
         out.writeBundle(mSnoozeCriteria);
         out.writeBundle(mShowBadge);
         out.writeBundle(mUserSentiment);
+        out.writeBundle(mHidden);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -151,4 +155,8 @@
     public Bundle getUserSentiment() {
         return mUserSentiment;
     }
+
+    public Bundle getHidden() {
+        return mHidden;
+    }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 740a387..3830b7a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -97,7 +97,7 @@
     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
             Policy.getAllSuppressedVisualEffects();
 
-    public static final int XML_VERSION = 5;
+    public static final int XML_VERSION = 6;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
@@ -516,11 +516,17 @@
         throw new IllegalStateException("Failed to reach END_DOCUMENT");
     }
 
-    public void writeXml(XmlSerializer out) throws IOException {
+    /**
+     * Writes XML of current ZenModeConfig
+     * @param out serializer
+     * @param version uses XML_VERSION if version is null
+     * @throws IOException
+     */
+    public void writeXml(XmlSerializer out, Integer version) throws IOException {
         out.startTag(null, ZEN_TAG);
-        out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
+        out.attribute(null, ZEN_ATT_VERSION, version == null
+                ? Integer.toString(XML_VERSION) : Integer.toString(version));
         out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
-
         out.startTag(null, ALLOW_TAG);
         out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
         out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 40e84b9..61277e2 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -545,7 +545,7 @@
      */
     public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) {
         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
-        if (um.isUserUnlocked()) {
+        if (um.isUserUnlocked(user)) {
             Slog.i(TAG, "User already unlocked");
             return;
         }
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 801d6e7..a107cab 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -688,7 +688,7 @@
     /**
      * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
      */
-    @IntRange(from = 0) int getMemoryUsage() {
+    public @IntRange(from = 0) int getMemoryUsage() {
         return nGetMemoryUsage(mNativePtr);
     }
 
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 74b199f..413df05 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.text.style.MetricAffectingSpan;
-import android.util.IntArray;
 
 import com.android.internal.util.Preconditions;
 
@@ -60,8 +59,10 @@
  * </pre>
  *
  * Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
+ * {@link android.widget.TextView} will be rejected.
+ *
+ * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
+ * PrecomputedText.
  */
 public class PrecomputedText implements Spannable {
     private static final char LINE_FEED = '\n';
@@ -269,6 +270,22 @@
         }
     };
 
+    /** @hide */
+    public static class ParagraphInfo {
+        public final @IntRange(from = 0) int paragraphEnd;
+        public final @NonNull MeasuredParagraph measured;
+
+        /**
+         * @param paraEnd the end offset of this paragraph
+         * @param measured a measured paragraph
+         */
+        public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) {
+            this.paragraphEnd = paraEnd;
+            this.measured = measured;
+        }
+    };
+
+
     // The original text.
     private final @NonNull SpannableString mText;
 
@@ -280,11 +297,8 @@
 
     private final @NonNull Params mParams;
 
-    // The measured paragraph texts.
-    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
-    // The sorted paragraph end offsets.
-    private final @NonNull int[] mParagraphBreakPoints;
+    // The list of measured paragraph info.
+    private final @NonNull ParagraphInfo[] mParagraphInfo;
 
     /**
      * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
@@ -294,29 +308,29 @@
      * presented can save work on the UI thread.
      * </p>
      *
+     * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the
+     * created PrecomputedText.
+     *
      * @param text the text to be measured
-     * @param param parameters that define how text will be precomputed
+     * @param params parameters that define how text will be precomputed
      * @return A {@link PrecomputedText}
      */
-    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
-        return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
+    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
+        ParagraphInfo[] paraInfo = createMeasuredParagraphs(
+                text, params, 0, text.length(), true /* computeLayout */);
+        return new PrecomputedText(text, 0, text.length(), params, paraInfo);
     }
 
     /** @hide */
-    public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        return createInternal(text, param, start, end, false /* compute width only */);
-    }
-
-    private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
+    public static ParagraphInfo[] createMeasuredParagraphs(
+            @NonNull CharSequence text, @NonNull Params params,
             @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
-        Preconditions.checkNotNull(text);
-        Preconditions.checkNotNull(param);
-        final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
-                && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+        ArrayList<ParagraphInfo> result = new ArrayList<>();
 
-        final IntArray paragraphEnds = new IntArray();
-        final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(params);
+        final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+                && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
 
         int paraEnd = 0;
         for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
@@ -329,27 +343,22 @@
                 paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
             }
 
-            paragraphEnds.add(paraEnd);
-            measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
-                    param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
-                    needHyphenation, computeLayout, null /* no recycle */));
+            result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+                    params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
+                    needHyphenation, computeLayout, null /* no recycle */)));
         }
-
-        return new PrecomputedText(text, start, end, param,
-                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
-                                paragraphEnds.toArray());
+        return result.toArray(new ParagraphInfo[result.size()]);
     }
 
     // Use PrecomputedText.create instead.
     private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Params param,
-            @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
-        mText = new SpannableString(text);
+            @IntRange(from = 0) int end, @NonNull Params params,
+            @NonNull ParagraphInfo[] paraInfo) {
+        mText = new SpannableString(text, true /* ignoreNoCopySpan */);
         mStart = start;
         mEnd = end;
-        mParams = param;
-        mMeasuredParagraphs = measuredTexts;
-        mParagraphBreakPoints = paragraphBreakPoints;
+        mParams = params;
+        mParagraphInfo = paraInfo;
     }
 
     /**
@@ -386,7 +395,7 @@
      * Returns the count of paragraphs.
      */
     public @IntRange(from = 0) int getParagraphCount() {
-        return mParagraphBreakPoints.length;
+        return mParagraphInfo.length;
     }
 
     /**
@@ -394,7 +403,7 @@
      */
     public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
         Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+        return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1);
     }
 
     /**
@@ -402,12 +411,17 @@
      */
     public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
         Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return mParagraphBreakPoints[paraIndex];
+        return mParagraphInfo[paraIndex].paragraphEnd;
     }
 
     /** @hide */
     public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
-        return mMeasuredParagraphs[paraIndex];
+        return mParagraphInfo[paraIndex].measured;
+    }
+
+    /** @hide */
+    public @NonNull ParagraphInfo[] getParagraphInfo() {
+        return mParagraphInfo;
     }
 
     /**
@@ -427,13 +441,13 @@
     public int findParaIndex(@IntRange(from = 0) int pos) {
         // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
         //       layout support to StaticLayout.
-        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
-            if (pos < mParagraphBreakPoints[i]) {
+        for (int i = 0; i < mParagraphInfo.length; ++i) {
+            if (pos < mParagraphInfo[i].paragraphEnd) {
                 return i;
             }
         }
         throw new IndexOutOfBoundsException(
-            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+            "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd
             + ", gave " + pos);
     }
 
diff --git a/core/java/android/text/SpannableString.java b/core/java/android/text/SpannableString.java
index 56d0946..afb5df8 100644
--- a/core/java/android/text/SpannableString.java
+++ b/core/java/android/text/SpannableString.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-
 /**
  * This is the class for text whose content is immutable but to which
  * markup objects can be attached and detached.
@@ -26,12 +25,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spannable
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannableString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannableString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannableString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public static SpannableString valueOf(CharSequence source) {
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 366ec14..5dd1a52 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -26,7 +26,7 @@
 /* package */ abstract class SpannableStringInternal
 {
     /* package */ SpannableStringInternal(CharSequence source,
-                                          int start, int end) {
+                                          int start, int end, boolean ignoreNoCopySpan) {
         if (start == 0 && end == source.length())
             mText = source.toString();
         else
@@ -38,24 +38,37 @@
 
         if (source instanceof Spanned) {
             if (source instanceof SpannableStringInternal) {
-                copySpans((SpannableStringInternal) source, start, end);
+                copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan);
             } else {
-                copySpans((Spanned) source, start, end);
+                copySpans((Spanned) source, start, end, ignoreNoCopySpan);
             }
         }
     }
 
     /**
+     * This unused method is left since this is listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    /* package */ SpannableStringInternal(CharSequence source, int start, int end) {
+        this(source, start, end, false /* ignoreNoCopySpan */);
+    }
+
+    /**
      * Copies another {@link Spanned} object's spans between [start, end] into this object.
      *
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
      */
-    private final void copySpans(Spanned src, int start, int end) {
+    private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
         Object[] spans = src.getSpans(start, end, Object.class);
 
         for (int i = 0; i < spans.length; i++) {
+            if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) {
+                continue;
+            }
             int st = src.getSpanStart(spans[i]);
             int en = src.getSpanEnd(spans[i]);
             int fl = src.getSpanFlags(spans[i]);
@@ -76,35 +89,48 @@
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons.
      */
-    private final void copySpans(SpannableStringInternal src, int start, int end) {
-        if (start == 0 && end == src.length()) {
+    private void copySpans(SpannableStringInternal src, int start, int end,
+            boolean ignoreNoCopySpan) {
+        int count = 0;
+        final int[] srcData = src.mSpanData;
+        final Object[] srcSpans = src.mSpans;
+        final int limit = src.mSpanCount;
+        boolean hasNoCopySpan = false;
+
+        for (int i = 0; i < limit; i++) {
+            int spanStart = srcData[i * COLUMNS + START];
+            int spanEnd = srcData[i * COLUMNS + END];
+            if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+            if (srcSpans[i] instanceof NoCopySpan) {
+                hasNoCopySpan = true;
+                if (ignoreNoCopySpan) {
+                    continue;
+                }
+            }
+            count++;
+        }
+
+        if (count == 0) return;
+
+        if (!hasNoCopySpan && start == 0 && end == src.length()) {
             mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
             mSpanData = new int[src.mSpanData.length];
             mSpanCount = src.mSpanCount;
             System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
             System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
         } else {
-            int count = 0;
-            int[] srcData = src.mSpanData;
-            int limit = src.mSpanCount;
-            for (int i = 0; i < limit; i++) {
-                int spanStart = srcData[i * COLUMNS + START];
-                int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
-                count++;
-            }
-
-            if (count == 0) return;
-
-            Object[] srcSpans = src.mSpans;
             mSpanCount = count;
             mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
             mSpanData = new int[mSpans.length * COLUMNS];
             for (int i = 0, j = 0; i < limit; i++) {
                 int spanStart = srcData[i * COLUMNS + START];
                 int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+                if (isOutOfCopyRange(start, end, spanStart, spanEnd)
+                        || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) {
+                    continue;
+                }
                 if (spanStart < start) spanStart = start;
                 if (spanEnd > end) spanEnd = end;
 
@@ -494,6 +520,21 @@
         return hash;
     }
 
+    /**
+     * Following two unused methods are left since these are listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    private void copySpans(Spanned src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+    private void copySpans(SpannableStringInternal src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+
+
     private String mText;
     private Object[] mSpans;
     private int[] mSpanData;
diff --git a/core/java/android/text/SpannedString.java b/core/java/android/text/SpannedString.java
index afed221..acee3c5 100644
--- a/core/java/android/text/SpannedString.java
+++ b/core/java/android/text/SpannedString.java
@@ -26,12 +26,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spanned
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannedString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannedString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannedString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public CharSequence subSequence(int start, int end) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 299bde2..0899074 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -651,31 +651,29 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        PrecomputedText measured = null;
-        final Spanned spanned;
+        PrecomputedText.ParagraphInfo[] paragraphInfo = null;
+        final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
         if (source instanceof PrecomputedText) {
-            measured = (PrecomputedText) source;
-            if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy,
-                      b.mHyphenationFrequency)) {
+            PrecomputedText precomputed = (PrecomputedText) source;
+            if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
+                      b.mBreakStrategy, b.mHyphenationFrequency)) {
                 // Some parameters are different from the ones when measured text is created.
-                measured = null;
+                paragraphInfo = precomputed.getParagraphInfo();
             }
         }
 
-        if (measured == null) {
+        if (paragraphInfo == null) {
             final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
                     b.mBreakStrategy, b.mHyphenationFrequency);
-            measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
-            spanned = (source instanceof Spanned) ? (Spanned) source : null;
-        } else {
-            final CharSequence original = measured.getText();
-            spanned = (original instanceof Spanned) ? (Spanned) original : null;
+            paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
+                    bufEnd, false /* computeLayout */);
         }
 
         try {
-            for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
-                final int paraStart = measured.getParagraphStart(paraIndex);
-                final int paraEnd = measured.getParagraphEnd(paraIndex);
+            for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+                final int paraStart = paraIndex == 0
+                        ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+                final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
 
                 int firstWidthLineCount = 1;
                 int firstWidth = outerWidth;
@@ -741,7 +739,7 @@
                     }
                 }
 
-                final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+                final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
                 final char[] chs = measuredPara.getChars();
                 final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
                 final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 3a22db2..435e324 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -650,7 +650,8 @@
         final CharSequence truncatedText = text.subSequence(
                 0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
 
-        final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options);
+        final Supplier<TextLinks> supplier = () ->
+                classifier.generateLinks(truncatedText, options.setLegacyFallback(true));
         final Consumer<TextLinks> consumer = links -> {
             if (links.getLinks().isEmpty()) {
                 if (callback != null) {
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index c44f42b..46e3169 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -219,7 +219,7 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     if (value == field.getInt(null)) {
-                        return field.getName().substring(prefix.length());
+                        return constNameWithoutPrefix(prefix, field);
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -236,6 +236,7 @@
      */
     public static String flagsToString(Class<?> clazz, String prefix, int flags) {
         final StringBuilder res = new StringBuilder();
+        boolean flagsWasZero = flags == 0;
 
         for (Field field : clazz.getDeclaredFields()) {
             final int modifiers = field.getModifiers();
@@ -243,9 +244,12 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     final int value = field.getInt(null);
+                    if (value == 0 && flagsWasZero) {
+                        return constNameWithoutPrefix(prefix, field);
+                    }
                     if ((flags & value) != 0) {
                         flags &= ~value;
-                        res.append(field.getName().substring(prefix.length())).append('|');
+                        res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -258,4 +262,8 @@
         }
         return res.toString();
     }
+
+    private static String constNameWithoutPrefix(String prefix, Field field) {
+        return field.getName().substring(prefix.length());
+    }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 38ab6f2..9687009 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,7 +42,7 @@
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
-        DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
+        DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
         DEFAULT_FLAGS.put("settings_audio_switcher", "false");
     }
 
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index ed2d3c6..30d7b6c 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.Network;
 import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.SystemClock;
@@ -80,6 +81,18 @@
 
     @Override
     public boolean forceRefresh() {
+        // We can't do this at initialization time: ConnectivityService might not be running yet.
+        synchronized (this) {
+            if (mCM == null) {
+                mCM = sContext.getSystemService(ConnectivityManager.class);
+            }
+        }
+
+        final Network network = mCM == null ? null : mCM.getActiveNetwork();
+        return forceRefresh(network);
+    }
+
+    public boolean forceRefresh(Network network) {
         if (TextUtils.isEmpty(mServer)) {
             // missing server, so no trusted time available
             return false;
@@ -88,11 +101,11 @@
         // We can't do this at initialization time: ConnectivityService might not be running yet.
         synchronized (this) {
             if (mCM == null) {
-                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+                mCM = sContext.getSystemService(ConnectivityManager.class);
             }
         }
 
-        final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
+        final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
         if (ni == null || !ni.isConnected()) {
             if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
             return false;
@@ -101,7 +114,7 @@
 
         if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
         final SntpClient client = new SntpClient();
-        if (client.requestTime(mServer, (int) mTimeout)) {
+        if (client.requestTime(mServer, (int) mTimeout, network)) {
             mHasCache = true;
             mCachedNtpTime = client.getNtpTime();
             mCachedNtpElapsedRealtime = client.getNtpTimeReference();
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 789131c..e8b4197 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -20,8 +20,7 @@
 
 /**
  * StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
- * statsd.
+ * define custom metrics inside statsd.
  */
 public final class StatsLog extends StatsLogInternal {
     private static final String TAG = "StatsManager";
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 9382741..2da353b 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -47,17 +47,25 @@
      * the WM has changed in the mean time but the client has not received onMovementBoundsChanged).
      */
     void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds,
-            boolean fromImeAdjustement, int displayRotation);
+            boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation);
 
     /**
      * Called when window manager decides to adjust the pinned stack bounds because of the IME, or
      * when the listener is first registered to allow the listener to synchronized its state with
      * the controller.  This call will always be followed by a onMovementBoundsChanged() call
-     * with fromImeAdjustement set to true.
+     * with fromImeAdjustement set to {@code true}.
      */
     void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
 
     /**
+     * Called when window manager decides to adjust the pinned stack bounds because of the shelf, or
+     * when the listener is first registered to allow the listener to synchronized its state with
+     * the controller.  This call will always be followed by a onMovementBoundsChanged() call
+     * with fromShelfAdjustment set to {@code true}.
+     */
+    void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight);
+
+    /**
      * Called when window manager decides to adjust the minimized state, or when the listener
      * is first registered to allow the listener to synchronized its state with the controller.
      */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d172fb5..6486230 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -284,7 +284,12 @@
      */
     oneway void setPipVisibility(boolean visible);
 
-   /**
+    /**
+     * Called by System UI to notify of changes to the visibility and height of the shelf.
+     */
+    void setShelfHeight(boolean visible, int shelfHeight);
+
+    /**
      * Called by System UI to enable or disable haptic feedback on the navigation bar buttons.
      */
     void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled);
@@ -295,8 +300,8 @@
     boolean hasNavigationBar();
 
     /**
-    * Get the position of the nav bar
-    */
+     * Get the position of the nav bar
+     */
     int getNavBarPosition();
 
     /**
@@ -423,4 +428,14 @@
      * on the next user activity.
      */
     void requestUserActivityNotification();
+
+    /**
+     * Notify WindowManager that it should not override the info in DisplayManager for the specified
+     * display. This can disable letter- or pillar-boxing applied in DisplayManager when the metrics
+     * of the logical display reported from WindowManager do not correspond to the metrics of the
+     * physical display it is based on.
+     *
+     * @param displayId The id of the display.
+     */
+    void dontOverrideDisplayInfo(int displayId);
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b7524fb..d4610a5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -353,8 +353,8 @@
         private int mFormat = PixelFormat.OPAQUE;
         private String mName;
         private SurfaceControl mParent;
-        private int mWindowType;
-        private int mOwnerUid;
+        private int mWindowType = -1;
+        private int mOwnerUid = -1;
 
         /**
          * Begin building a SurfaceControl with a given {@link SurfaceSession}.
@@ -566,7 +566,7 @@
      */
     private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
             SurfaceControl parent, int windowType, int ownerUid)
-                    throws OutOfResourcesException {
+                    throws OutOfResourcesException, IllegalArgumentException {
         if (session == null) {
             throw new IllegalArgumentException("session must not be null");
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7213923..f930f6e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -179,6 +179,8 @@
 
     private int mPendingReportDraws;
 
+    private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
+
     public SurfaceView(Context context) {
         this(context, null);
     }
@@ -774,21 +776,34 @@
         });
     }
 
+    /**
+     * A place to over-ride for applying child-surface transactions.
+     * These can be synchronized with the viewroot surface using deferTransaction.
+     *
+     * Called from RenderWorker while UI thread is paused.
+     * @hide
+     */
+    protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
+            Surface viewRootSurface, long nextViewRootFrameNumber) {
+    }
+
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
         ViewRootImpl viewRoot = getViewRootImpl();
 
-        SurfaceControl.openTransaction();
-        try {
-            if (frameNumber > 0) {
-                mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
-            }
-            mSurfaceControl.setPosition(position.left, position.top);
-            mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
-                    0.0f, 0.0f,
-                    position.height() / (float) mSurfaceHeight);
-        } finally {
-            SurfaceControl.closeTransaction();
+        if (frameNumber > 0) {
+            mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
+                    frameNumber);
         }
+        mRtTransaction.setPosition(mSurfaceControl,position.left, position.top);
+        mRtTransaction.setMatrix(mSurfaceControl,
+                position.width() / (float) mSurfaceWidth,
+                0.0f, 0.0f,
+                position.height() / (float) mSurfaceHeight);
+
+        applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
+                frameNumber);
+
+        mRtTransaction.apply();
     }
 
     private Rect mRTLastReportedPosition = new Rect();
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8d076f7..5eb7e9c 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -20,13 +20,11 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import android.os.Build;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -936,6 +934,20 @@
         nSetHighContrastText(highContrastText);
     }
 
+    /**
+     * If set RenderThread will avoid doing any IPC using instead a fake vsync & DisplayInfo source
+     */
+    public static void setIsolatedProcess(boolean isIsolated) {
+        nSetIsolatedProcess(isIsolated);
+    }
+
+    /**
+     * If set extra graphics debugging abilities will be enabled such as dumping skp
+     */
+    public static void setDebuggingEnabled(boolean enable) {
+        nSetDebuggingEnabled(enable);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1071,10 +1083,6 @@
             initSched(renderProxy);
 
             if (mAppContext != null) {
-                final boolean appDebuggable =
-                        (mAppContext.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)
-                        != 0;
-                nSetDebuggingEnabled(appDebuggable || Build.IS_DEBUGGABLE);
                 initGraphicsStats();
             }
         }
@@ -1204,4 +1212,5 @@
     // For temporary experimentation b/66945974
     private static native void nHackySetRTAnimationsEnabled(boolean enabled);
     private static native void nSetDebuggingEnabled(boolean enabled);
+    private static native void nSetIsolatedProcess(boolean enabled);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b624870..afff19b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8910,7 +8910,11 @@
                 if (node != null) {
                     return node.isVisibleToUser();
                 }
+                // if node is null, assume it's not visible anymore
+            } else {
+                Log.w(VIEW_LOG_TAG, "isVisibleToUserForAutofill(" + virtualId + "): no provider");
             }
+            return false;
         }
         return true;
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8e60a72..33fcf6a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4285,6 +4285,13 @@
                 recreateChildDisplayList(child);
             }
         }
+        final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View child = mTransientViews.get(i);
+            if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
+                recreateChildDisplayList(child);
+            }
+        }
         if (mOverlay != null) {
             View overlayView = mOverlay.getOverlayView();
             recreateChildDisplayList(overlayView);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 69938cb..abc19d0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2882,9 +2882,8 @@
         /**
          * @hide
          */
-        public String toString(String prefix) {
-            StringBuilder sb = new StringBuilder(256);
-            sb.append("{(");
+        public void dumpDimensions(StringBuilder sb) {
+            sb.append('(');
             sb.append(x);
             sb.append(',');
             sb.append(y);
@@ -2895,6 +2894,15 @@
             sb.append((height == MATCH_PARENT ? "fill" : (height == WRAP_CONTENT
                     ? "wrap" : String.valueOf(height))));
             sb.append(")");
+        }
+
+        /**
+         * @hide
+         */
+        public String toString(String prefix) {
+            StringBuilder sb = new StringBuilder(256);
+            sb.append('{');
+            dumpDimensions(sb);
             if (horizontalMargin != 0) {
                 sb.append(" hm=");
                 sb.append(horizontalMargin);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index fdd3f73..4c437dd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3858,6 +3858,7 @@
         builder.append("; password: ").append(isPassword());
         builder.append("; scrollable: ").append(isScrollable());
         builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
+        builder.append("; visible: ").append(isVisibleToUser());
         builder.append("; actions: ").append(mActions);
 
         return builder.toString();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c109297..5bee87c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1413,7 +1413,7 @@
      */
     public boolean isAutofillUiShowing() {
         final AutofillClient client = mContext.getAutofillClient();
-        return client != null & client.autofillClientIsFillUiShowing();
+        return client != null && client.autofillClientIsFillUiShowing();
     }
 
     /** @hide */
@@ -2540,6 +2540,10 @@
             ArraySet<AutofillId> updatedVisibleTrackedIds = null;
             ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
             if (client != null) {
+                if (sVerbose) {
+                    Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
+                            + " vis=" + mVisibleTrackedIds);
+                }
                 if (mInvisibleTrackedIds != null) {
                     final ArrayList<AutofillId> orderedInvisibleIds =
                             new ArrayList<>(mInvisibleTrackedIds);
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index c783cae..2a24dd7 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -30,6 +30,8 @@
 import android.service.textclassifier.ITextSelectionCallback;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.Preconditions;
 
 import java.util.concurrent.CountDownLatch;
@@ -37,8 +39,10 @@
 
 /**
  * Proxy to the system's default TextClassifier.
+ * @hide
  */
-final class SystemTextClassifier implements TextClassifier {
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class SystemTextClassifier implements TextClassifier {
 
     private static final String LOG_TAG = "SystemTextClassifier";
 
@@ -53,13 +57,13 @@
     @GuardedBy("mLoggerLock")
     private Logger mLogger;
 
-    SystemTextClassifier(Context context, TextClassificationConstants settings)
+    public SystemTextClassifier(Context context, TextClassificationConstants settings)
                 throws ServiceManager.ServiceNotFoundException {
         mManagerService = ITextClassifierService.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
         mSettings = Preconditions.checkNotNull(settings);
         mFallback = new TextClassifierImpl(context, settings);
-        mPackageName = context.getPackageName();
+        mPackageName = Preconditions.checkNotNull(context.getPackageName());
     }
 
     /**
@@ -124,8 +128,9 @@
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
         Utils.validate(text, false /* allowInMainThread */);
 
-        if (!mSettings.isSmartLinkifyEnabled()) {
-            return TextClassifier.NO_OP.generateLinks(text, options);
+        final boolean legacyFallback = options != null && options.isLegacyFallback();
+        if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
+            return Utils.generateLegacyLinks(text, options);
         }
 
         try {
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index c91116a..b5c9de9 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -344,6 +344,25 @@
         }
     }
 
+    /**
+     * Triggers the specified intent.
+     *
+     * @throws IllegalArgumentException if context or intent is null
+     * @hide
+     */
+    public static void fireIntent(@NonNull final Context context, @NonNull final Intent intent) {
+        switch (getIntentType(intent, context)) {
+            case IntentType.ACTIVITY:
+                context.startActivity(intent);
+                return;
+            case IntentType.SERVICE:
+                context.startService(intent);
+                return;
+            default:
+                return;
+        }
+    }
+
     @IntentType
     private static int getIntentType(@NonNull Intent intent, @NonNull Context context) {
         Preconditions.checkArgument(context != null);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 887bebb..98fa574 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -26,6 +26,12 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.URLSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -37,6 +43,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Interface for providing text classification related features.
@@ -511,6 +518,65 @@
             Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
         }
 
+        /**
+         * Generates links using legacy {@link Linkify}.
+         */
+        public static TextLinks generateLegacyLinks(
+                @NonNull CharSequence text, @NonNull TextLinks.Options options) {
+            final String string = Preconditions.checkNotNull(text).toString();
+            final TextLinks.Builder links = new TextLinks.Builder(string);
+
+            final List<String> entities = Preconditions.checkNotNull(options).getEntityConfig()
+                    .resolveEntityListModifications(Collections.emptyList());
+            if (entities.contains(TextClassifier.TYPE_URL)) {
+                addLinks(links, string, TextClassifier.TYPE_URL);
+            }
+            if (entities.contains(TextClassifier.TYPE_PHONE)) {
+                addLinks(links, string, TextClassifier.TYPE_PHONE);
+            }
+            if (entities.contains(TextClassifier.TYPE_EMAIL)) {
+                addLinks(links, string, TextClassifier.TYPE_EMAIL);
+            }
+            // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+            return links.build();
+        }
+
+        private static void addLinks(
+                TextLinks.Builder links, String string, @EntityType String entityType) {
+            final Spannable spannable = new SpannableString(string);
+            if (Linkify.addLinks(spannable, linkMask(entityType))) {
+                final URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
+                for (URLSpan urlSpan : spans) {
+                    links.addLink(
+                            spannable.getSpanStart(urlSpan),
+                            spannable.getSpanEnd(urlSpan),
+                            entityScores(entityType),
+                            urlSpan);
+                }
+            }
+        }
+
+        @LinkifyMask
+        private static int linkMask(@EntityType String entityType) {
+            switch (entityType) {
+                case TextClassifier.TYPE_URL:
+                    return Linkify.WEB_URLS;
+                case TextClassifier.TYPE_PHONE:
+                    return Linkify.PHONE_NUMBERS;
+                case TextClassifier.TYPE_EMAIL:
+                    return Linkify.EMAIL_ADDRESSES;
+                default:
+                    // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+                    return 0;
+            }
+        }
+
+        private static Map<String, Float> entityScores(@EntityType String entityType) {
+            final Map<String, Float> scores = new ArrayMap<>();
+            scores.put(entityType, 1f);
+            return scores;
+        }
+
         private static void checkMainThread(boolean allowInMainThread) {
             if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
                 Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index a099820..c2fb032 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -209,13 +209,15 @@
     public TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
         Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
+
+        final boolean legacyFallback = options != null && options.isLegacyFallback();
+        if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
+            return Utils.generateLegacyLinks(text, options);
+        }
+
         final String textString = text.toString();
         final TextLinks.Builder builder = new TextLinks.Builder(textString);
 
-        if (!mSettings.isSmartLinkifyEnabled()) {
-            return builder.build();
-        }
-
         try {
             final long startTimeMs = System.currentTimeMillis();
             final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
@@ -355,12 +357,10 @@
         final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
 
         ModelFile bestModel = null;
-        int bestModelVersion = -1;
         for (ModelFile model : listAllModelsLocked()) {
             if (model.isAnyLanguageSupported(languageRangeList)) {
-                if (model.getVersion() >= bestModelVersion) {
+                if (model.isPreferredTo(bestModel)) {
                     bestModel = model;
-                    bestModelVersion = model.getVersion();
                 }
             }
         }
@@ -482,6 +482,7 @@
         private final String mName;
         private final int mVersion;
         private final List<Locale> mSupportedLocales;
+        private final boolean mLanguageIndependent;
 
         /** Returns null if the path did not point to a compatible model. */
         static @Nullable ModelFile fromPath(String path) {
@@ -496,12 +497,14 @@
                     Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
                     return null;
                 }
+                final boolean languageIndependent = supportedLocalesStr.equals("*");
                 final List<Locale> supportedLocales = new ArrayList<>();
                 for (String langTag : supportedLocalesStr.split(",")) {
                     supportedLocales.add(Locale.forLanguageTag(langTag));
                 }
                 closeAndLogError(modelFd);
-                return new ModelFile(path, file.getName(), version, supportedLocales);
+                return new ModelFile(path, file.getName(), version, supportedLocales,
+                                     languageIndependent);
             } catch (FileNotFoundException e) {
                 Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
                 return null;
@@ -525,7 +528,7 @@
 
         /** Returns whether the language supports any language in the given ranges. */
         boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
-            return Locale.lookup(languageRanges, mSupportedLocales) != null;
+            return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
         }
 
         /** All locales supported by the model. */
@@ -533,6 +536,25 @@
             return Collections.unmodifiableList(mSupportedLocales);
         }
 
+        public boolean isPreferredTo(ModelFile model) {
+            // A model is preferred to no model.
+            if (model == null) {
+                return true;
+            }
+
+            // A language-specific model is preferred to a language independent
+            // model.
+            if (!mLanguageIndependent && model.mLanguageIndependent) {
+                return true;
+            }
+
+            // A higher-version model is preferred.
+            if (getVersion() > model.getVersion()) {
+                return true;
+            }
+            return false;
+        }
+
         @Override
         public boolean equals(Object other) {
             if (this == other) {
@@ -555,11 +577,13 @@
                     mPath, mName, mVersion, localesJoiner.toString());
         }
 
-        private ModelFile(String path, String name, int version, List<Locale> supportedLocales) {
+        private ModelFile(String path, String name, int version, List<Locale> supportedLocales,
+                          boolean languageIndependent) {
             mPath = path;
             mName = name;
             mVersion = version;
             mSupportedLocales = supportedLocales;
+            mLanguageIndependent = languageIndependent;
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index af9fc7d..38a7d9a 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,17 +20,21 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.Spannable;
 import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
 import android.text.util.Linkify;
 import android.text.util.Linkify.LinkifyMask;
 import android.view.View;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -197,6 +201,7 @@
         private final EntityConfidence mEntityScores;
         private final int mStart;
         private final int mEnd;
+        @Nullable final URLSpan mUrlSpan;
 
         /**
          * Create a new TextLink.
@@ -204,16 +209,19 @@
          * @param start The start index of the identified subsequence
          * @param end The end index of the identified subsequence
          * @param entityScores A mapping of entity type to confidence score
+         * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
          *
          * @throws IllegalArgumentException if entityScores is null or empty
          */
-        TextLink(int start, int end, Map<String, Float> entityScores) {
+        TextLink(int start, int end, Map<String, Float> entityScores,
+                @Nullable URLSpan urlSpan) {
             Preconditions.checkNotNull(entityScores);
             Preconditions.checkArgument(!entityScores.isEmpty());
             Preconditions.checkArgument(start <= end);
             mStart = start;
             mEnd = end;
             mEntityScores = new EntityConfidence(entityScores);
+            mUrlSpan = urlSpan;
         }
 
         /**
@@ -291,6 +299,7 @@
             mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
             mStart = in.readInt();
             mEnd = in.readInt();
+            mUrlSpan = null;
         }
     }
 
@@ -301,6 +310,7 @@
 
         private LocaleList mDefaultLocales;
         private TextClassifier.EntityConfig mEntityConfig;
+        private boolean mLegacyFallback;
 
         private @ApplyStrategy int mApplyStrategy;
         private Function<TextLink, TextLinkSpan> mSpanFactory;
@@ -354,6 +364,17 @@
         }
 
         /**
+         * Sets whether the TextClassifier can fallback to legacy links if smart linkify is
+         * disabled.
+         * <strong>Note: </strong>This is not parcelled.
+         * @hide
+         */
+        public Options setLegacyFallback(boolean legacyFallback) {
+            mLegacyFallback = legacyFallback;
+            return this;
+        }
+
+        /**
          * Sets a strategy for resolving conflicts when applying generated links to text that
          * already have links.
          *
@@ -406,6 +427,16 @@
         }
 
         /**
+         * Returns whether the TextClassifier can fallback to legacy links if smart linkify is
+         * disabled.
+         * <strong>Note: </strong>This is not parcelled.
+         * @hide
+         */
+        public boolean isLegacyFallback() {
+            return mLegacyFallback;
+        }
+
+        /**
          * @return the strategy for resolving conflictswhen applying generated links to text that
          * already have links
          *
@@ -497,7 +528,7 @@
 
         private final TextLink mTextLink;
 
-        public TextLinkSpan(@Nullable TextLink textLink) {
+        public TextLinkSpan(@NonNull TextLink textLink) {
             mTextLink = textLink;
         }
 
@@ -505,13 +536,38 @@
         public void onClick(View widget) {
             if (widget instanceof TextView) {
                 final TextView textView = (TextView) widget;
-                textView.requestActionMode(this);
+                final Context context = textView.getContext();
+                if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
+                    if (textView.requestFocus()) {
+                        textView.requestActionMode(this);
+                    } else {
+                        // If textView can not take focus, then simply handle the click as it will
+                        // be difficult to get rid of the floating action mode.
+                        textView.handleClick(this);
+                    }
+                } else {
+                    if (mTextLink.mUrlSpan != null) {
+                        mTextLink.mUrlSpan.onClick(textView);
+                    } else {
+                        textView.handleClick(this);
+                    }
+                }
             }
         }
 
         public final TextLink getTextLink() {
             return mTextLink;
         }
+
+        /** @hide */
+        @VisibleForTesting(visibility = Visibility.PRIVATE)
+        @Nullable
+        public final String getUrl() {
+            if (mTextLink.mUrlSpan != null) {
+                return mTextLink.mUrlSpan.getURL();
+            }
+            return null;
+        }
     }
 
     /**
@@ -534,12 +590,24 @@
         /**
          * Adds a TextLink.
          *
-         * @return this instance
+         * @param start The start index of the identified subsequence
+         * @param end The end index of the identified subsequence
+         * @param entityScores A mapping of entity type to confidence score
          *
          * @throws IllegalArgumentException if entityScores is null or empty.
          */
         public Builder addLink(int start, int end, Map<String, Float> entityScores) {
-            mLinks.add(new TextLink(start, end, entityScores));
+            mLinks.add(new TextLink(start, end, entityScores, null));
+            return this;
+        }
+
+        /**
+         * @see #addLink(int, int, Map)
+         * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
+         */
+        Builder addLink(int start, int end, Map<String, Float> entityScores,
+                @Nullable URLSpan urlSpan) {
+            mLinks.add(new TextLink(start, end, entityScores, urlSpan));
             return this;
         }
 
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 779eefb..886f5c8 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -445,9 +445,15 @@
         private void processOrEnqueueTask(SpellCheckerParams scp) {
             ISpellCheckerSession session;
             synchronized (this) {
+                if (scp.mWhat == TASK_CLOSE && (mState == STATE_CLOSED_AFTER_CONNECTION
+                        || mState == STATE_CLOSED_BEFORE_CONNECTION)) {
+                    // It is OK to call SpellCheckerSession#close() multiple times.
+                    // Don't output confusing/misleading warning messages.
+                    return;
+                }
                 if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) {
                     Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState="
-                            + taskToString(scp.mWhat)
+                            + stateToString(mState)
                             + " scp.mWhat=" + taskToString(scp.mWhat));
                     return;
                 }
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
index 68badec..d95ca61 100644
--- a/core/java/android/webkit/TracingConfig.java
+++ b/core/java/android/webkit/TracingConfig.java
@@ -35,9 +35,9 @@
     private @TracingMode int mTracingMode;
 
     /** @hide */
-    @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER,
-            CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING,
-            CATEGORIES_FRAME_VIEWER})
+    @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_ALL, CATEGORIES_ANDROID_WEBVIEW,
+            CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING,
+            CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface PredefinedCategories {}
 
@@ -90,34 +90,28 @@
     public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
 
     /** @hide */
-    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER})
+    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TracingMode {}
 
     /**
-     * Record trace events until the internal tracing buffer is full. Default tracing mode.
-     * Typically the buffer memory usage is between {@link #RECORD_CONTINUOUSLY} and the
-     * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. Depending on the implementation typically allows
-     * up to 256k events to be stored.
+     * Record trace events until the internal tracing buffer is full.
+     *
+     * Typically the buffer memory usage is larger than {@link #RECORD_CONTINUOUSLY}.
+     * Depending on the implementation typically allows up to 256k events to be stored.
      */
     public static final int RECORD_UNTIL_FULL = 0;
 
     /**
-     * Record trace events continuously using an internal ring buffer. Overwrites
-     * old events if they exceed buffer capacity. Uses less memory than both
-     * {@link #RECORD_UNTIL_FULL} and {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} modes.
-     * Depending on the implementation typically allows up to 64k events to be stored.
+     * Record trace events continuously using an internal ring buffer. Default tracing mode.
+     *
+     * Overwrites old events if they exceed buffer capacity. Uses less memory than the
+     * {@link #RECORD_UNTIL_FULL} mode. Depending on the implementation typically allows
+     * up to 64k events to be stored.
      */
     public static final int RECORD_CONTINUOUSLY = 1;
 
     /**
-     * Record trace events using a larger internal tracing buffer until it is full.
-     * Uses significantly more memory than {@link #RECORD_UNTIL_FULL} and may not be
-     * suitable on devices with smaller RAM.
-     */
-    public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
-
-    /**
      * @hide
      */
     public TracingConfig(@PredefinedCategories int predefinedCategories,
@@ -182,7 +176,7 @@
     public static class Builder {
         private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
         private final List<String> mCustomIncludedCategories = new ArrayList<String>();
-        private @TracingMode int mTracingMode = RECORD_UNTIL_FULL;
+        private @TracingMode int mTracingMode = RECORD_CONTINUOUSLY;
 
         /**
          * Default constructor for Builder.
@@ -202,7 +196,9 @@
          *
          * @param predefinedCategories list or bitmask of predefined category sets to use:
          *                    {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
-         *                    {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+         *                    {@link #CATEGORIES_ANDROID_WEBVIEW},
+         *                    {@link #CATEGORIES_WEB_DEVELOPER},
+         *                    {@link #CATEGORIES_INPUT_LATENCY},
          *                    {@link #CATEGORIES_RENDERING},
          *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
          *                    {@link #CATEGORIES_FRAME_VIEWER}.
@@ -250,9 +246,8 @@
         /**
          * Sets the tracing mode for this configuration.
          *
-         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
-         *                    {@link #RECORD_CONTINUOUSLY} or
-         *                    {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}.
+         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
+         *                    {@link #RECORD_CONTINUOUSLY}.
          * @return The builder to facilitate chaining.
          */
         public Builder setTracingMode(@TracingMode int tracingMode) {
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 7871021..50068f5 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -60,9 +60,8 @@
      * Starts tracing all webviews. Depending on the trace mode in traceConfig
      * specifies how the trace events are recorded.
      *
-     * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
-     * {@link TracingConfig#RECORD_CONTINUOUSLY} and
-     * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
+     * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL} and
+     * {@link TracingConfig#RECORD_CONTINUOUSLY} the events are recorded
      * using an internal buffer and flushed to the outputStream when
      * {@link #stop(OutputStream, Executor)} is called.
      *
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5178a97..fc94b1f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2451,6 +2451,14 @@
      * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
      */
     @NonNull
+    public Looper getWebViewLooper() {
+        return mWebViewThread;
+    }
+
+    /**
+     * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
+     */
+    @NonNull
     public Looper getLooper() {
         return mWebViewThread;
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9a99963..298c61e 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -89,7 +89,7 @@
 
 /**
  * Base class that can be used to implement virtualized lists of items. A list does
- * not have a spatial definition here. For instance, subclases of this class can
+ * not have a spatial definition here. For instance, subclasses of this class can
  * display the content of the list in a grid, in a carousel, as stack, etc.
  *
  * @attr ref android.R.styleable#AbsListView_listSelector
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index e2601dc..df04beb 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -215,6 +215,10 @@
                 mWindow.destroy();
                 mWindow = null;
             }
+            mPrevPosInView.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevPosInView.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevStartCoordsInSurface.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevStartCoordsInSurface.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
         }
     }
 
@@ -321,19 +325,24 @@
 
         // Clamp copy coordinates inside the surface to avoid displaying distorted content.
         final int clampedStartXInSurface = Math.max(0,
-                Math.min(startXInSurface, surfaceWidth - mWindowWidth));
+                Math.min(startXInSurface, surfaceWidth - mBitmapWidth));
         final int clampedStartYInSurface = Math.max(0,
-                Math.min(startYInSurface, surfaceHeight - mWindowHeight));
+                Math.min(startYInSurface, surfaceHeight - mBitmapHeight));
+
+        // Clamp window coordinates inside the parent surface, to avoid displaying
+        // the magnifier out of screen or overlapping with system insets.
+        final Rect insets = mView.getRootWindowInsets().getSystemWindowInsets();
+        final int windowCoordsX = Math.max(insets.left,
+                Math.min(surfaceWidth - mWindowWidth - insets.right, mWindowCoords.x));
+        final int windowCoordsY = Math.max(insets.top,
+                Math.min(surfaceHeight - mWindowHeight - insets.bottom, mWindowCoords.y));
 
         // Perform the pixel copy.
         mPixelCopyRequestRect.set(clampedStartXInSurface,
                 clampedStartYInSurface,
                 clampedStartXInSurface + mBitmapWidth,
                 clampedStartYInSurface + mBitmapHeight);
-        final int windowCoordsX = mWindowCoords.x;
-        final int windowCoordsY = mWindowCoords.y;
         final InternalPopupWindow currentWindowInstance = mWindow;
-
         final Bitmap bitmap =
                 Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
         PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 3ec8ab9..f52854a 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -33,6 +33,7 @@
 
 // TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden.
 /**
+ * @hide
  * A View that contains the controls for MediaPlayer2.
  * It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
  * "Subtitle", "Full Screen", and it is also possible to add multiple custom buttons.
@@ -150,7 +151,7 @@
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super((instance, superProvider, privateProvider) ->
-                ApiLoader.getProvider(context).createMediaControlView2(
+                ApiLoader.getProvider().createMediaControlView2(
                         (MediaControlView2) instance, superProvider, privateProvider,
                         attrs, defStyleAttr, defStyleRes),
                 context, attrs, defStyleAttr, defStyleRes);
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index be8c34c..6e855ba 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -246,7 +246,7 @@
                 mTextView.invalidate();
             }
             mTextClassification = result.mClassification;
-        } else if (actionMode == Editor.TextActionMode.TEXT_LINK) {
+        } else if (result != null && actionMode == Editor.TextActionMode.TEXT_LINK) {
             mTextClassification = result.mClassification;
         } else {
             mTextClassification = null;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1e2d18c..c366a91 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -162,6 +162,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
@@ -187,6 +188,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * A user interface element that displays text to the user.
@@ -1918,19 +1923,12 @@
             // Calculate the sizes set based on minimum size, maximum size and step size if we do
             // not have a predefined set of sizes or if the current sizes array is empty.
             if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
-                int autoSizeValuesLength = 1;
-                float currentSize = Math.round(mAutoSizeMinTextSizeInPx);
-                while (Math.round(currentSize + mAutoSizeStepGranularityInPx)
-                        <= Math.round(mAutoSizeMaxTextSizeInPx)) {
-                    autoSizeValuesLength++;
-                    currentSize += mAutoSizeStepGranularityInPx;
-                }
-
-                int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
-                float sizeToAdd = mAutoSizeMinTextSizeInPx;
+                final int autoSizeValuesLength = ((int) Math.floor((mAutoSizeMaxTextSizeInPx
+                        - mAutoSizeMinTextSizeInPx) / mAutoSizeStepGranularityInPx)) + 1;
+                final int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
                 for (int i = 0; i < autoSizeValuesLength; i++) {
-                    autoSizeTextSizesInPx[i] = Math.round(sizeToAdd);
-                    sizeToAdd += mAutoSizeStepGranularityInPx;
+                    autoSizeTextSizesInPx[i] = Math.round(
+                            mAutoSizeMinTextSizeInPx + (i * mAutoSizeStepGranularityInPx));
                 }
                 mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
             }
@@ -11523,16 +11521,13 @@
     }
 
     /**
-     * Starts an ActionMode for the specified TextLink.
+     * Starts an ActionMode for the specified TextLinkSpan.
      *
      * @return Whether or not we're attempting to start the action mode.
      * @hide
      */
     public boolean requestActionMode(@NonNull TextLinks.TextLinkSpan clickedSpan) {
         Preconditions.checkNotNull(clickedSpan);
-        final TextLinks.TextLink link = clickedSpan.getTextLink();
-        Preconditions.checkNotNull(link);
-        createEditorIfNeeded();
 
         if (!(mText instanceof Spanned)) {
             return false;
@@ -11541,15 +11536,55 @@
         final int start = ((Spanned) mText).getSpanStart(clickedSpan);
         final int end = ((Spanned) mText).getSpanEnd(clickedSpan);
 
-        if (start < 0 || end < 1) {
+        if (start < 0 || end > mText.length() || start >= end) {
             return false;
         }
 
+        createEditorIfNeeded();
         mEditor.startLinkActionModeAsync(start, end);
         return true;
     }
 
     /**
+     * Handles a click on the specified TextLinkSpan.
+     *
+     * @return Whether or not the click is being handled.
+     * @hide
+     */
+    public boolean handleClick(@NonNull TextLinks.TextLinkSpan clickedSpan) {
+        Preconditions.checkNotNull(clickedSpan);
+        if (mText instanceof Spanned) {
+            final Spanned spanned = (Spanned) mText;
+            final int start = spanned.getSpanStart(clickedSpan);
+            final int end = spanned.getSpanEnd(clickedSpan);
+            if (start >= 0 && end <= mText.length() && start < end) {
+                final TextClassification.Options options = new TextClassification.Options()
+                        .setDefaultLocales(getTextLocales());
+                final Supplier<TextClassification> supplier = () ->
+                        getTextClassifier().classifyText(mText, start, end, options);
+                final Consumer<TextClassification> consumer = classification -> {
+                    if (classification != null) {
+                        final Intent intent = classification.getIntent();
+                        if (intent != null) {
+                            TextClassification.fireIntent(mContext, intent);
+                        } else {
+                            Log.d(LOG_TAG, "No link action to perform");
+                        }
+                    } else {
+                        // classification == null
+                        Log.d(LOG_TAG, "Timeout while classifying text");
+                    }
+                };
+                CompletableFuture.supplyAsync(supplier)
+                        .completeOnTimeout(null, 1, TimeUnit.SECONDS)
+                        .thenAccept(consumer);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * @hide
      */
     protected void stopTextActionMode() {
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 6f08dc2..388eae2 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -47,6 +47,7 @@
 
 // TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted.
 /**
+ * @hide
  * Displays a video file.  VideoView2 class is a View class which is wrapping {@link MediaPlayer2}
  * so that developers can easily implement a video rendering application.
  *
@@ -143,7 +144,7 @@
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super((instance, superProvider, privateProvider) ->
-                ApiLoader.getProvider(context).createVideoView2(
+                ApiLoader.getProvider().createVideoView2(
                         (VideoView2) instance, superProvider, privateProvider,
                         attrs, defStyleAttr, defStyleRes),
                 context, attrs, defStyleAttr, defStyleRes);
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 1bee692..93730df 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -17,8 +17,10 @@
 package com.android.internal.app;
 
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
 import android.os.ParcelUuid;
 
 /**
@@ -44,9 +46,15 @@
     int startRecognitionForIntent(in ParcelUuid soundModelId, in PendingIntent callbackIntent,
          in SoundTrigger.RecognitionConfig config);
 
+
+    int startRecognitionForService(in ParcelUuid soundModelId, in Bundle params,
+         in ComponentName callbackIntent,in SoundTrigger.RecognitionConfig config);
+
+    /** For both ...Intent and ...Service based usage */
     int stopRecognitionForIntent(in ParcelUuid soundModelId);
 
     int unloadSoundModel(in ParcelUuid soundModelId);
 
+    /** For both ...Intent and ...Service based usage */
     boolean isRecognitionActive(in ParcelUuid parcelUuid);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index d3b4dbf..9a082ec 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -320,8 +320,8 @@
         return builder;
     }
 
-    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context,
-            final ArrayList<InputMethodInfo> imis) {
+    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
+            Context context, ArrayList<InputMethodInfo> imis, boolean onlyMinimum) {
         final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
         // We will primarily rely on the system locale, but also keep relying on the fallback locale
         // as a last resort.
@@ -329,11 +329,19 @@
         // then pick up suitable auxiliary IMEs when necessary (e.g. Voice IMEs with "automatic"
         // subtype)
         final Locale systemLocale = getSystemLocaleFromContext(context);
-        return getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale)
-                .fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
-                        true /* checkCountry */, SUBTYPE_MODE_ANY)
-                .fillAuxiliaryImes(imis, context)
-                .build();
+        final InputMethodListBuilder builder =
+                getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale);
+        if (!onlyMinimum) {
+            builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+                    true /* checkCountry */, SUBTYPE_MODE_ANY)
+                    .fillAuxiliaryImes(imis, context);
+        }
+        return builder.build();
+    }
+
+    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
+            Context context, ArrayList<InputMethodInfo> imis) {
+        return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
     }
 
     public static Locale constructLocaleFromString(String localeStr) {
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 5950436..21b7d25 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -55,10 +55,13 @@
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
         List<NotificationChannel> channelsList = new ArrayList<NotificationChannel>();
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel keyboard = new NotificationChannel(
                 VIRTUAL_KEYBOARD,
                 context.getString(R.string.notification_channel_virtual_keyboard),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        keyboard.setBypassDnd(true);
+        keyboard.setBlockableSystem(true);
+        channelsList.add(keyboard);
 
         final NotificationChannel physicalKeyboardChannel = new NotificationChannel(
                 PHYSICAL_KEYBOARD,
@@ -66,81 +69,105 @@
                 NotificationManager.IMPORTANCE_DEFAULT);
         physicalKeyboardChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        physicalKeyboardChannel.setBlockableSystem(true);
         channelsList.add(physicalKeyboardChannel);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel security = new NotificationChannel(
                 SECURITY,
                 context.getString(R.string.notification_channel_security),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        security.setBypassDnd(true);
+        channelsList.add(security);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel car = new NotificationChannel(
                 CAR_MODE,
                 context.getString(R.string.notification_channel_car_mode),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        car.setBlockableSystem(true);
+        car.setBypassDnd(true);
+        channelsList.add(car);
 
         channelsList.add(newAccountChannel(context));
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel developer = new NotificationChannel(
                 DEVELOPER,
                 context.getString(R.string.notification_channel_developer),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        developer.setBypassDnd(true);
+        developer.setBlockableSystem(true);
+        channelsList.add(developer);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel updates = new NotificationChannel(
                 UPDATES,
                 context.getString(R.string.notification_channel_updates),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        updates.setBypassDnd(true);
+        channelsList.add(updates);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel network = new NotificationChannel(
                 NETWORK_STATUS,
                 context.getString(R.string.notification_channel_network_status),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        network.setBypassDnd(true);
+        channelsList.add(network);
 
         final NotificationChannel networkAlertsChannel = new NotificationChannel(
                 NETWORK_ALERTS,
                 context.getString(R.string.notification_channel_network_alerts),
                 NotificationManager.IMPORTANCE_HIGH);
-        networkAlertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
-                Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        networkAlertsChannel.setBypassDnd(true);
+        networkAlertsChannel.setBlockableSystem(true);
         channelsList.add(networkAlertsChannel);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel networkAvailable = new NotificationChannel(
                 NETWORK_AVAILABLE,
                 context.getString(R.string.notification_channel_network_available),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        networkAvailable.setBlockableSystem(true);
+        networkAvailable.setBypassDnd(true);
+        channelsList.add(networkAvailable);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel vpn = new NotificationChannel(
                 VPN,
                 context.getString(R.string.notification_channel_vpn),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        vpn.setBypassDnd(true);
+        channelsList.add(vpn);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel deviceAdmin = new NotificationChannel(
                 DEVICE_ADMIN,
                 context.getString(R.string.notification_channel_device_admin),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        deviceAdmin.setBypassDnd(true);
+        channelsList.add(deviceAdmin);
 
         final NotificationChannel alertsChannel = new NotificationChannel(
                 ALERTS,
                 context.getString(R.string.notification_channel_alerts),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        alertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
-                Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        alertsChannel.setBypassDnd(true);
         channelsList.add(alertsChannel);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel retail = new NotificationChannel(
                 RETAIL_MODE,
                 context.getString(R.string.notification_channel_retail_mode),
-                NotificationManager.IMPORTANCE_LOW));
+                NotificationManager.IMPORTANCE_LOW);
+        retail.setBypassDnd(true);
+        channelsList.add(retail);
 
-        channelsList.add(new NotificationChannel(
+        final NotificationChannel usb = new NotificationChannel(
                 USB,
                 context.getString(R.string.notification_channel_usb),
-                NotificationManager.IMPORTANCE_MIN));
+                NotificationManager.IMPORTANCE_MIN);
+        usb.setBypassDnd(true);
+        channelsList.add(usb);
 
         NotificationChannel foregroundChannel = new NotificationChannel(
                 FOREGROUND_SERVICE,
                 context.getString(R.string.notification_channel_foreground_service),
                 NotificationManager.IMPORTANCE_LOW);
         foregroundChannel.setBlockableSystem(true);
+        foregroundChannel.setBypassDnd(true);
         channelsList.add(foregroundChannel);
 
         NotificationChannel heavyWeightChannel = new NotificationChannel(
@@ -152,16 +179,19 @@
                 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                 .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
                 .build());
+        heavyWeightChannel.setBypassDnd(true);
         channelsList.add(heavyWeightChannel);
 
         NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES,
                 context.getString(R.string.notification_channel_system_changes),
                 NotificationManager.IMPORTANCE_LOW);
+        systemChanges.setBypassDnd(true);
         channelsList.add(systemChanges);
 
         NotificationChannel dndChanges = new NotificationChannel(DO_NOT_DISTURB,
                 context.getString(R.string.notification_channel_do_not_disturb),
                 NotificationManager.IMPORTANCE_LOW);
+        dndChanges.setBypassDnd(true);
         channelsList.add(dndChanges);
 
         nm.createNotificationChannels(channelsList);
@@ -178,10 +208,12 @@
     }
 
     private static NotificationChannel newAccountChannel(Context context) {
-        return new NotificationChannel(
+        final NotificationChannel acct = new NotificationChannel(
                 ACCOUNT,
                 context.getString(R.string.notification_channel_account),
                 NotificationManager.IMPORTANCE_LOW);
+        acct.setBypassDnd(true);
+        return acct;
     }
 
     private SystemNotificationChannels() {}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4ab2fec..3c150c1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
 import android.content.ContentResolver;
@@ -33,9 +34,6 @@
 import android.os.BatteryManager;
 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;
 import android.os.IBatteryPropertiesRegistrar;
@@ -53,6 +51,9 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
+import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
@@ -90,8 +91,8 @@
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 
-import java.util.List;
 import libcore.util.EmptyArray;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -109,11 +110,11 @@
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Queue;
 import java.util.concurrent.Future;
-import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -137,7 +138,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 176 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 177 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -234,11 +235,15 @@
     protected final SparseIntArray mPendingUids = new SparseIntArray();
 
     @GuardedBy("this")
-    private long mNumCpuTimeReads;
+    private long mNumSingleUidCpuTimeReads;
     @GuardedBy("this")
-    private long mNumBatchedCpuTimeReads;
+    private long mNumBatchedSingleUidCpuTimeReads;
     @GuardedBy("this")
     private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis();
+    @GuardedBy("this")
+    private int mNumUidsRemoved;
+    @GuardedBy("this")
+    private int mNumAllUidCpuTimeReads;
 
     /** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
     private final RpmStats mTmpRpmStats = new RpmStats();
@@ -246,6 +251,67 @@
     private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
     /** Last time that RPM stats were updated by updateRpmStatsLocked. */
     private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+    /**
+     * Use a queue to delay removing UIDs from {@link KernelUidCpuTimeReader},
+     * {@link KernelUidCpuActiveTimeReader}, {@link KernelUidCpuClusterTimeReader},
+     * {@link KernelUidCpuFreqTimeReader} and from the Kernel.
+     *
+     * Isolated and invalid UID info must be removed to conserve memory. However, STATSD and
+     * Batterystats both need to access UID cpu time. To resolve this race condition, only
+     * Batterystats shall remove UIDs, and a delay {@link Constants#UID_REMOVE_DELAY_MS} is
+     * implemented so that STATSD can capture those UID times before they are deleted.
+     */
+    @GuardedBy("this")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
+
+    @VisibleForTesting
+    public final class UidToRemove {
+        int startUid;
+        int endUid;
+        long timeAddedInQueue;
+
+        /** Remove just one UID */
+        public UidToRemove(int uid, long timestamp) {
+            this(uid, uid, timestamp);
+        }
+
+        /** Remove a range of UIDs, startUid must be smaller than endUid. */
+        public UidToRemove(int startUid, int endUid, long timestamp) {
+            this.startUid = startUid;
+            this.endUid = endUid;
+            timeAddedInQueue = timestamp;
+        }
+
+        void remove() {
+            if (startUid == endUid) {
+                mKernelUidCpuTimeReader.removeUid(startUid);
+                mKernelUidCpuFreqTimeReader.removeUid(startUid);
+                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                    mKernelUidCpuActiveTimeReader.removeUid(startUid);
+                    mKernelUidCpuClusterTimeReader.removeUid(startUid);
+                }
+                if (mKernelSingleUidTimeReader != null) {
+                    mKernelSingleUidTimeReader.removeUid(startUid);
+                }
+                mNumUidsRemoved++;
+            } else if (startUid < endUid) {
+                mKernelUidCpuFreqTimeReader.removeUidsInRange(startUid, endUid);
+                mKernelUidCpuTimeReader.removeUidsInRange(startUid, endUid);
+                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                    mKernelUidCpuActiveTimeReader.removeUidsInRange(startUid, endUid);
+                    mKernelUidCpuClusterTimeReader.removeUidsInRange(startUid, endUid);
+                }
+                if (mKernelSingleUidTimeReader != null) {
+                    mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
+                }
+                // Treat as one. We don't know how many uids there are in between.
+                mNumUidsRemoved++;
+            } else {
+                Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+            }
+        }
+    }
 
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
@@ -376,6 +442,14 @@
         }
     }
 
+    public void clearPendingRemovedUids() {
+        long cutOffTime = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
+        while (!mPendingRemovedUids.isEmpty()
+                && mPendingRemovedUids.peek().timeAddedInQueue < cutOffTime) {
+            mPendingRemovedUids.poll().remove();
+        }
+    }
+
     public void copyFromAllUidsCpuTimes() {
         synchronized (BatteryStatsImpl.this) {
             copyFromAllUidsCpuTimes(
@@ -1477,27 +1551,31 @@
         }
     }
 
+    @VisibleForTesting
     public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
         final TimeBase mTimeBase;
-        long mCount;
-        long mLoadedCount;
-        long mUnpluggedCount;
+        public long mCount;
+        public long mCurrentCount;
+        public long mLoadedCount;
+        public long mUnpluggedCount;
 
-        LongSamplingCounter(TimeBase timeBase, Parcel in) {
+        public LongSamplingCounter(TimeBase timeBase, Parcel in) {
             mTimeBase = timeBase;
             mCount = in.readLong();
+            mCurrentCount = in.readLong();
             mLoadedCount = in.readLong();
             mUnpluggedCount = in.readLong();
             timeBase.add(this);
         }
 
-        LongSamplingCounter(TimeBase timeBase) {
+        public LongSamplingCounter(TimeBase timeBase) {
             mTimeBase = timeBase;
             timeBase.add(this);
         }
 
         public void writeToParcel(Parcel out) {
             out.writeLong(mCount);
+            out.writeLong(mCurrentCount);
             out.writeLong(mLoadedCount);
             out.writeLong(mUnpluggedCount);
         }
@@ -1524,24 +1602,37 @@
         @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount
+                    + " mCurrentCount=" + mCurrentCount
                     + " mLoadedCount=" + mLoadedCount
                     + " mUnpluggedCount=" + mUnpluggedCount);
         }
 
-        void addCountLocked(long count) {
-            addCountLocked(count, mTimeBase.isRunning());
+        public void addCountLocked(long count) {
+            update(mCurrentCount + count, mTimeBase.isRunning());
         }
 
-        void addCountLocked(long count, boolean isRunning) {
-            if (isRunning) {
-                mCount += count;
+        public void addCountLocked(long count, boolean isRunning) {
+            update(mCurrentCount + count, isRunning);
+        }
+
+        public void update(long count) {
+            update(count, mTimeBase.isRunning());
+        }
+
+        public void update(long count, boolean isRunning) {
+            if (count < mCurrentCount) {
+                mCurrentCount = 0;
             }
+            if (isRunning) {
+                mCount += count - mCurrentCount;
+            }
+            mCurrentCount = count;
         }
 
         /**
          * Clear state of this counter.
          */
-        void reset(boolean detachIfReset) {
+        public void reset(boolean detachIfReset) {
             mCount = 0;
             mLoadedCount = mUnpluggedCount = 0;
             if (detachIfReset) {
@@ -1549,18 +1640,16 @@
             }
         }
 
-        void detach() {
+        public void detach() {
             mTimeBase.remove(this);
         }
 
-        void writeSummaryFromParcelLocked(Parcel out) {
+        public void writeSummaryFromParcelLocked(Parcel out) {
             out.writeLong(mCount);
         }
 
-        void readSummaryFromParcelLocked(Parcel in) {
-            mLoadedCount = in.readLong();
-            mCount = mLoadedCount;
-            mUnpluggedCount = mLoadedCount;
+        public void readSummaryFromParcelLocked(Parcel in) {
+            mCount = mUnpluggedCount= mLoadedCount = in.readLong();
         }
     }
 
@@ -3961,12 +4050,7 @@
             u.removeIsolatedUid(isolatedUid);
             mIsolatedUids.removeAt(idx);
         }
-        mKernelUidCpuTimeReader.removeUid(isolatedUid);
-        mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
-        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
-            mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
-            mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
-        }
+        mPendingRemovedUids.add(new UidToRemove(isolatedUid, mClocks.elapsedRealtime()));
     }
 
     public int mapUid(int uid) {
@@ -5284,69 +5368,15 @@
     }
 
     public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData) {
+        // BatteryStats uses 0 to represent no network type.
+        // Telephony does not have a concept of no network type, and uses 0 to represent unknown.
+        // Unknown is included in DATA_CONNECTION_OTHER.
         int bin = DATA_CONNECTION_NONE;
         if (hasData) {
-            switch (dataType) {
-                case TelephonyManager.NETWORK_TYPE_EDGE:
-                    bin = DATA_CONNECTION_EDGE;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_GPRS:
-                    bin = DATA_CONNECTION_GPRS;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_UMTS:
-                    bin = DATA_CONNECTION_UMTS;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_CDMA:
-                    bin = DATA_CONNECTION_CDMA;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_EVDO_0:
-                    bin = DATA_CONNECTION_EVDO_0;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_EVDO_A:
-                    bin = DATA_CONNECTION_EVDO_A;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_1xRTT:
-                    bin = DATA_CONNECTION_1xRTT;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_HSDPA:
-                    bin = DATA_CONNECTION_HSDPA;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_HSUPA:
-                    bin = DATA_CONNECTION_HSUPA;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_HSPA:
-                    bin = DATA_CONNECTION_HSPA;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_IDEN:
-                    bin = DATA_CONNECTION_IDEN;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                    bin = DATA_CONNECTION_EVDO_B;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_LTE:
-                    bin = DATA_CONNECTION_LTE;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_EHRPD:
-                    bin = DATA_CONNECTION_EHRPD;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_HSPAP:
-                    bin = DATA_CONNECTION_HSPAP;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_GSM:
-                    bin = DATA_CONNECTION_GSM;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                    bin = DATA_CONNECTION_TD_SCDMA;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_IWLAN:
-                    bin = DATA_CONNECTION_IWLAN;
-                    break;
-                case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                    bin = DATA_CONNECTION_LTE_CA;
-                    break;
-                default:
-                    bin = DATA_CONNECTION_OTHER;
-                    break;
+            if (dataType > 0 && dataType <= TelephonyManager.MAX_NETWORK_TYPE) {
+                bin = dataType;
+            } else {
+                bin = DATA_CONNECTION_OTHER;
             }
         }
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
@@ -9860,9 +9890,9 @@
                                     mBsi.mOnBatteryTimeBase.isRunning(),
                                     mBsi.mOnBatteryScreenOffTimeBase.isRunning(),
                                     mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS);
-                            mBsi.mNumCpuTimeReads++;
+                            mBsi.mNumSingleUidCpuTimeReads++;
                         } else {
-                            mBsi.mNumBatchedCpuTimeReads++;
+                            mBsi.mNumBatchedSingleUidCpuTimeReads++;
                         }
                         if (mBsi.mPendingUids.indexOfKey(mUid) < 0
                                 || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
@@ -10031,7 +10061,8 @@
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
                 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                        name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
+                        name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
+                        JobProtoEnums.STOP_REASON_CANCELLED);
             }
         }
 
@@ -10041,7 +10072,8 @@
                 t.stopRunningLocked(elapsedRealtimeMs);
                 if (!t.isRunningLocked()) { // only tell statsd if truly stopped
                     StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                            name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
+                            name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
+                            stopReason);
                 }
             }
             if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -11024,6 +11056,9 @@
         mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
         mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
 
+        mNumAllUidCpuTimeReads = 0;
+        mNumUidsRemoved = 0;
+
         initDischarge();
 
         clearHistoryLocked();
@@ -11583,10 +11618,6 @@
         }
     }
 
-    // Cache last value for comparison.
-    private BluetoothActivityEnergyInfo mLastBluetoothActivityEnergyInfo =
-            new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
-
     /**
      * Add modem tx power to history
      * Device is said to be in high cellular transmit power when it has spent most of the transmit
@@ -11625,8 +11656,35 @@
         return;
     }
 
+    private final class BluetoothActivityInfoCache {
+        long idleTimeMs;
+        long rxTimeMs;
+        long txTimeMs;
+        long energy;
+
+        SparseLongArray uidRxBytes = new SparseLongArray();
+        SparseLongArray uidTxBytes = new SparseLongArray();
+
+        void set(BluetoothActivityEnergyInfo info) {
+            idleTimeMs = info.getControllerIdleTimeMillis();
+            rxTimeMs = info.getControllerRxTimeMillis();
+            txTimeMs = info.getControllerTxTimeMillis();
+            energy = info.getControllerEnergyUsed();
+            if (info.getUidTraffic() != null) {
+                for (UidTraffic traffic : info.getUidTraffic()) {
+                    uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
+                    uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
+                }
+            }
+        }
+    }
+
+    private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
+            = new BluetoothActivityInfoCache();
+
     /**
      * Distribute Bluetooth energy info and network traffic to apps.
+     *
      * @param info The energy information from the bluetooth controller.
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
@@ -11641,12 +11699,13 @@
         mHasBluetoothReporting = true;
 
         final long elapsedRealtimeMs = mClocks.elapsedRealtime();
-        final long rxTimeMs = info.getControllerRxTimeMillis() -
-                mLastBluetoothActivityEnergyInfo.getControllerRxTimeMillis();
-        final long txTimeMs = info.getControllerTxTimeMillis() -
-                mLastBluetoothActivityEnergyInfo.getControllerTxTimeMillis();
-        final long idleTimeMs = info.getControllerIdleTimeMillis() -
-                mLastBluetoothActivityEnergyInfo.getControllerIdleTimeMillis();
+        final long rxTimeMs =
+                info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
+        final long txTimeMs =
+                info.getControllerTxTimeMillis() - mLastBluetoothActivityInfo.txTimeMs;
+        final long idleTimeMs =
+                info.getControllerIdleTimeMillis() - mLastBluetoothActivityInfo.idleTimeMs;
+
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "------ BEGIN BLE power blaming ------");
             Slog.d(TAG, "  Tx Time:    " + txTimeMs + " ms");
@@ -11718,8 +11777,8 @@
         }
 
         if (DEBUG_ENERGY) {
-            Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs
-                    + " TX=" + leftOverTxTimeMs);
+            Slog.d(TAG, "Left over time for traffic RX=" + leftOverRxTimeMs + " TX="
+                    + leftOverTxTimeMs);
         }
 
         //
@@ -11730,70 +11789,56 @@
         long totalRxBytes = 0;
 
         final UidTraffic[] uidTraffic = info.getUidTraffic();
-        final UidTraffic[] lastUidTraffic = mLastBluetoothActivityEnergyInfo.getUidTraffic();
-        final ArrayList<UidTraffic> deltaTraffic = new ArrayList<>();
-        int m = 0, n = 0;
-        for (; m < uidTraffic.length && n < lastUidTraffic.length; m++) {
-            final UidTraffic traffic = uidTraffic[m];
-            final UidTraffic lastTraffic = lastUidTraffic[n];
-            if (traffic.getUid() == lastTraffic.getUid()) {
-                deltaTraffic.add(new UidTraffic(traffic.getUid(),
-                        traffic.getRxBytes() - lastTraffic.getRxBytes(),
-                        traffic.getTxBytes() - lastTraffic.getTxBytes()));
-                n++;
-            }
-        }
-        for (; m < uidTraffic.length; m ++) {
-            deltaTraffic.add(uidTraffic[m]);
-        }
-
-        for (int i = 0, j = 0; i < deltaTraffic.size(); i++) {
-            final UidTraffic traffic = deltaTraffic.get(i);
+        final int numUids = uidTraffic != null ? uidTraffic.length : 0;
+        for (int i = 0; i < numUids; i++) {
+            final UidTraffic traffic = uidTraffic[i];
+            final long rxBytes = traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(
+                    traffic.getUid());
+            final long txBytes = traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(
+                    traffic.getUid());
 
             // Add to the global counters.
-            mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(
-                    traffic.getRxBytes());
-            mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(
-                    traffic.getTxBytes());
+            mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(rxBytes);
+            mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(txBytes);
 
             // Add to the UID counters.
             final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
-            u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, traffic.getRxBytes(), 0);
-            u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, traffic.getTxBytes(), 0);
+            u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, rxBytes, 0);
+            u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, txBytes, 0);
 
             // Calculate the total traffic.
-            totalTxBytes += traffic.getTxBytes();
-            totalRxBytes += traffic.getRxBytes();
+            totalRxBytes += rxBytes;
+            totalTxBytes += txBytes;
         }
 
-        if ((totalTxBytes != 0 || totalRxBytes != 0) &&
-                (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) {
-            for (int i = 0; i < deltaTraffic.size(); i++) {
-                final UidTraffic traffic = deltaTraffic.get(i);
+        if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
+                || leftOverTxTimeMs != 0)) {
+            for (int i = 0; i < numUids; i++) {
+                final UidTraffic traffic = uidTraffic[i];
+                final int uid = traffic.getUid();
+                final long rxBytes =
+                        traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(uid);
+                final long txBytes =
+                        traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(uid);
 
-                final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
+                final Uid u = getUidStatsLocked(mapUid(uid));
                 final ControllerActivityCounterImpl counter =
                         u.getOrCreateBluetoothControllerActivityLocked();
 
-                if (totalRxBytes > 0 && traffic.getRxBytes() > 0) {
-                    final long timeRxMs = (leftOverRxTimeMs * traffic.getRxBytes()) / totalRxBytes;
-
+                if (totalRxBytes > 0 && rxBytes > 0) {
+                    final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
                     if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + traffic.getUid() + " rx_bytes=" + traffic.getRxBytes()
-                                + " rx_time=" + timeRxMs);
+                        Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
                     }
                     counter.getRxTimeCounter().addCountLocked(timeRxMs);
                     leftOverRxTimeMs -= timeRxMs;
                 }
 
-                if (totalTxBytes > 0 && traffic.getTxBytes() > 0) {
-                    final long timeTxMs = (leftOverTxTimeMs * traffic.getTxBytes()) / totalTxBytes;
-
+                if (totalTxBytes > 0 && txBytes > 0) {
+                    final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
                     if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + traffic.getUid() + " tx_bytes=" + traffic.getTxBytes()
-                                + " tx_time=" + timeTxMs);
+                        Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
                     }
-
                     counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
                     leftOverTxTimeMs -= timeTxMs;
                 }
@@ -11810,10 +11855,10 @@
         if (opVolt != 0) {
             // We store the power drain as mAms.
             mBluetoothActivity.getPowerCounter().addCountLocked(
-                    (long) ((info.getControllerEnergyUsed() -
-                            mLastBluetoothActivityEnergyInfo.getControllerEnergyUsed() )/ opVolt));
+                    (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+                            / opVolt));
         }
-        mLastBluetoothActivityEnergyInfo = info;
+        mLastBluetoothActivityInfo.set(info);
     }
 
     /**
@@ -12009,9 +12054,11 @@
         if (!onBattery) {
             mKernelUidCpuTimeReader.readDelta(null);
             mKernelUidCpuFreqTimeReader.readDelta(null);
+            mNumAllUidCpuTimeReads += 2;
             if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
                 mKernelUidCpuActiveTimeReader.readDelta(null);
                 mKernelUidCpuClusterTimeReader.readDelta(null);
+                mNumAllUidCpuTimeReads += 2;
             }
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
@@ -12029,9 +12076,11 @@
             updateClusterSpeedTimes(updatedUids, onBattery);
         }
         readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff);
+        mNumAllUidCpuTimeReads += 2;
         if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
             readKernelUidCpuActiveTimesLocked(onBattery);
             readKernelUidCpuClusterTimesLocked(onBattery);
+            mNumAllUidCpuTimeReads += 2;
         }
     }
 
@@ -13256,11 +13305,8 @@
     public void onCleanupUserLocked(int userId) {
         final int firstUidForUser = UserHandle.getUid(userId, 0);
         final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
-        mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        if (mKernelSingleUidTimeReader != null) {
-            mKernelSingleUidTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        }
+        mPendingRemovedUids.add(
+                new UidToRemove(firstUidForUser, lastUidForUser, mClocks.elapsedRealtime()));
     }
 
     public void onUserRemovedLocked(int userId) {
@@ -13277,12 +13323,8 @@
      * Remove the statistics object for a particular uid.
      */
     public void removeUidStatsLocked(int uid) {
-        mKernelUidCpuTimeReader.removeUid(uid);
-        mKernelUidCpuFreqTimeReader.removeUid(uid);
-        if (mKernelSingleUidTimeReader != null) {
-            mKernelSingleUidTimeReader.removeUid(uid);
-        }
         mUidStats.remove(uid);
+        mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
     }
 
     /**
@@ -13335,24 +13377,24 @@
                 = "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";
         public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
                 = "proc_state_cpu_times_read_delay_ms";
         public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME
                 = "kernel_uid_readers_throttle_time";
+        public static final String KEY_UID_REMOVE_DELAY_MS
+                = "uid_remove_delay_ms";
 
         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 = true;
         private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
+        private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
 
         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;
-        public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
         public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
         public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME;
+        public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13390,14 +13432,14 @@
                                 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);
-                updateReadBinaryCpuTime(READ_BINARY_CPU_TIME,
-                        mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME));
                 updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                         mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                                 DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
                 updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME,
                         mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME,
                                 DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
+                updateUidRemoveDelay(
+                        mParser.getLong(KEY_UID_REMOVE_DELAY_MS, DEFAULT_UID_REMOVE_DELAY_MS));
             }
         }
 
@@ -13407,24 +13449,17 @@
                 mKernelSingleUidTimeReader.markDataAsStale(true);
                 mExternalSync.scheduleCpuSyncDueToSettingChange();
 
-                mNumCpuTimeReads = 0;
-                mNumBatchedCpuTimeReads = 0;
+                mNumSingleUidCpuTimeReads = 0;
+                mNumBatchedSingleUidCpuTimeReads = 0;
                 mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
             }
         }
 
-        private void updateReadBinaryCpuTime(boolean oldEnabled, boolean isEnabled) {
-            READ_BINARY_CPU_TIME = isEnabled;
-            if (oldEnabled != isEnabled) {
-                mKernelUidCpuFreqTimeReader.setReadBinary(isEnabled);
-            }
-        }
-
         private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
             PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
             if (oldDelayMillis != newDelayMillis) {
-                mNumCpuTimeReads = 0;
-                mNumBatchedCpuTimeReads = 0;
+                mNumSingleUidCpuTimeReads = 0;
+                mNumBatchedSingleUidCpuTimeReads = 0;
                 mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
             }
         }
@@ -13440,13 +13475,16 @@
             }
         }
 
+        private void updateUidRemoveDelay(long newTimeMs) {
+            UID_REMOVE_DELAY_MS = newTimeMs;
+            clearPendingRemovedUids();
+        }
+
         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);
             pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
             pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
             pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
@@ -13459,6 +13497,43 @@
         mConstants.dumpLocked(pw);
     }
 
+    @GuardedBy("this")
+    public void dumpCpuStatsLocked(PrintWriter pw) {
+        int size = mUidStats.size();
+        pw.println("Per UID CPU user & system time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            Uid uid = mUidStats.get(u);
+            pw.print("  "); pw.print(u); pw.print(": ");
+            pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
+            pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
+        }
+        pw.println("Per UID CPU active time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            Uid uid = mUidStats.get(u);
+            if (uid.getCpuActiveTime() > 0) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(uid.getCpuActiveTime());
+            }
+        }
+        pw.println("Per UID CPU cluster time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            long[] times = mUidStats.get(u).getCpuClusterTimes();
+            if (times != null) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
+            }
+        }
+        pw.println("Per UID CPU frequency time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            long[] times = mUidStats.get(u).getCpuFreqTimes(STATS_SINCE_CHARGED);
+            if (times != null) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
+            }
+        }
+    }
+
     Parcel mPendingWrite = null;
     final ReentrantLock mWriteLock = new ReentrantLock();
 
@@ -15183,10 +15258,14 @@
         }
         super.dumpLocked(context, pw, flags, reqUid, histStart);
         pw.print("Total cpu time reads: ");
-        pw.println(mNumCpuTimeReads);
+        pw.println(mNumSingleUidCpuTimeReads);
         pw.print("Batched cpu time reads: ");
-        pw.println(mNumBatchedCpuTimeReads);
+        pw.println(mNumBatchedSingleUidCpuTimeReads);
         pw.print("Batching Duration (min): ");
         pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (60 * 1000));
+        pw.print("All UID cpu time reads since the later of device start or stats reset: ");
+        pw.println(mNumAllUidCpuTimeReads);
+        pw.print("UIDs removed since the later of device start or stats reset: ");
+        pw.println(mNumUidsRemoved);
     }
 }
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
new file mode 100644
index 0000000..2c48506
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -0,0 +1,272 @@
+/*
+ * 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.internal.os;
+
+import android.os.Binder;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Collects statistics about CPU time spent per binder call across multiple dimensions, e.g.
+ * per thread, uid or call description.
+ */
+public class BinderCallsStats {
+    private static final int CALL_SESSIONS_POOL_SIZE = 100;
+    private static final BinderCallsStats sInstance = new BinderCallsStats();
+
+    private volatile boolean mTrackingEnabled = false;
+    private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
+    private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
+
+    private BinderCallsStats() {
+    }
+
+    @VisibleForTesting
+    public BinderCallsStats(boolean trackingEnabled) {
+        mTrackingEnabled = trackingEnabled;
+    }
+
+    public CallSession callStarted(Binder binder, int code) {
+        if (!mTrackingEnabled) {
+            return null;
+        }
+
+        return callStarted(binder.getClass().getName(), code);
+    }
+
+    private CallSession callStarted(String className, int code) {
+        CallSession s = mCallSessionsPool.poll();
+        if (s == null) {
+            s = new CallSession();
+        }
+        s.mCallStat.className = className;
+        s.mCallStat.msg = code;
+
+        s.mStarted = getThreadTimeMicro();
+        return s;
+    }
+
+    public void callEnded(CallSession s) {
+        if (!mTrackingEnabled) {
+            return;
+        }
+        Preconditions.checkNotNull(s);
+        final long cpuTimeNow = getThreadTimeMicro();
+        final long duration = cpuTimeNow - s.mStarted;
+        s.mCallingUId = Binder.getCallingUid();
+
+        synchronized (mUidEntries) {
+            UidEntry uidEntry = mUidEntries.get(s.mCallingUId);
+            if (uidEntry == null) {
+                uidEntry = new UidEntry(s.mCallingUId);
+                mUidEntries.put(s.mCallingUId, uidEntry);
+            }
+
+            // Find CallDesc entry and update its total time
+            CallStat callStat = uidEntry.mCallStats.get(s.mCallStat);
+            // Only create CallStat if it's a new entry, otherwise update existing instance
+            if (callStat == null) {
+                callStat = new CallStat(s.mCallStat.className, s.mCallStat.msg);
+                uidEntry.mCallStats.put(callStat, callStat);
+            }
+            uidEntry.time += duration;
+            uidEntry.callCount++;
+            callStat.callCount++;
+            callStat.time += duration;
+        }
+        if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
+            mCallSessionsPool.add(s);
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        Map<Integer, Long> uidTimeMap = new HashMap<>();
+        Map<Integer, Long> uidCallCountMap = new HashMap<>();
+        long totalCallsCount = 0;
+        long totalCallsTime = 0;
+        int uidEntriesSize = mUidEntries.size();
+        List<UidEntry> entries = new ArrayList<>();
+        synchronized (mUidEntries) {
+            for (int i = 0; i < uidEntriesSize; i++) {
+                UidEntry e = mUidEntries.valueAt(i);
+                entries.add(e);
+                totalCallsTime += e.time;
+                // Update per-uid totals
+                Long totalTimePerUid = uidTimeMap.get(e.uid);
+                uidTimeMap.put(e.uid,
+                        totalTimePerUid == null ? e.time : totalTimePerUid + e.time);
+                Long totalCallsPerUid = uidCallCountMap.get(e.uid);
+                uidCallCountMap.put(e.uid, totalCallsPerUid == null ? e.callCount
+                        : totalCallsPerUid + e.callCount);
+                totalCallsCount += e.callCount;
+            }
+        }
+        pw.println("Binder call stats:");
+        pw.println("  Raw data (uid,call_desc,time):");
+        entries.sort((o1, o2) -> {
+            if (o1.time < o2.time) {
+                return 1;
+            } else if (o1.time > o2.time) {
+                return -1;
+            }
+            return 0;
+        });
+        StringBuilder sb = new StringBuilder();
+        for (UidEntry uidEntry : entries) {
+            List<CallStat> callStats = new ArrayList<>(uidEntry.mCallStats.keySet());
+            callStats.sort((o1, o2) -> {
+                if (o1.time < o2.time) {
+                    return 1;
+                } else if (o1.time > o2.time) {
+                    return -1;
+                }
+                return 0;
+            });
+            for (CallStat e : callStats) {
+                sb.setLength(0);
+                sb.append("    ")
+                        .append(uidEntry.uid).append(",").append(e).append(',').append(e.time);
+                pw.println(sb);
+            }
+        }
+        pw.println();
+        pw.println("  Per UID Summary(UID: time, total_time_percentage, calls_count):");
+        List<Map.Entry<Integer, Long>> uidTotals = new ArrayList<>(uidTimeMap.entrySet());
+        uidTotals.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
+        for (Map.Entry<Integer, Long> uidTotal : uidTotals) {
+            Long callCount = uidCallCountMap.get(uidTotal.getKey());
+            pw.println(String.format("    %5d: %11d %3.0f%% %8d",
+                    uidTotal.getKey(), uidTotal.getValue(),
+                    100d * uidTotal.getValue() / totalCallsTime, callCount));
+        }
+        pw.println();
+        pw.println(String.format("  Summary: total_time=%d, "
+                        + "calls_count=%d, avg_call_time=%.0f",
+                totalCallsTime, totalCallsCount,
+                (double)totalCallsTime / totalCallsCount));
+    }
+
+    private static long getThreadTimeMicro() {
+        return SystemClock.currentThreadTimeMicro();
+    }
+
+    public static BinderCallsStats getInstance() {
+        return sInstance;
+    }
+
+    public void setTrackingEnabled(boolean enabled) {
+        mTrackingEnabled = enabled;
+    }
+
+    public boolean isTrackingEnabled() {
+        return mTrackingEnabled;
+    }
+
+    private static class CallStat {
+        String className;
+        int msg;
+        long time;
+        long callCount;
+
+        CallStat() {
+        }
+
+        CallStat(String className, int msg) {
+            this.className = className;
+            this.msg = msg;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            CallStat callStat = (CallStat) o;
+
+            return msg == callStat.msg && (className == callStat.className);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = className.hashCode();
+            result = 31 * result + msg;
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return className + "/" + msg;
+        }
+    }
+
+    public static class CallSession {
+        int mCallingUId;
+        long mStarted;
+        CallStat mCallStat = new CallStat();
+    }
+
+    private static class UidEntry {
+        int uid;
+        long time;
+        long callCount;
+
+        UidEntry(int uid) {
+            this.uid = uid;
+        }
+
+        // Aggregate time spent per each call name: call_desc -> cpu_time_micros
+        Map<CallStat, CallStat> mCallStats = new ArrayMap<>();
+
+        @Override
+        public String toString() {
+            return "UidEntry{" +
+                    "time=" + time +
+                    ", callCount=" + callCount +
+                    ", mCallStats=" + mCallStats +
+                    '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            UidEntry uidEntry = (UidEntry) o;
+            return uid == uidEntry.uid;
+        }
+
+        @Override
+        public int hashCode() {
+            return uid;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
index e790e08..bd8a67a 100644
--- a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -24,6 +24,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
@@ -54,6 +55,7 @@
 
     private final KernelCpuProcReader mProcReader;
     private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
+    private int mCores;
 
     public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
         /**
@@ -75,7 +77,60 @@
     }
 
     @Override
-    protected void readDeltaImpl(@Nullable Callback cb) {
+    protected void readDeltaImpl(@Nullable Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double activeTime = sumActiveTime(buf);
+            if (activeTime > 0) {
+                double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
+                if (delta > 0) {
+                    mLastUidCpuActiveTimeMs.put(uid, activeTime);
+                    if (callback != null) {
+                        callback.onUidCpuActiveTime(uid, (long) delta);
+                    }
+                } else if (delta < 0) {
+                    Slog.e(TAG, "Negative delta from active time proc: " + delta);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double activeTime = sumActiveTime(buf);
+            if (activeTime > 0) {
+                callback.onUidCpuActiveTime(uid, (long) activeTime);
+            }
+        });
+    }
+
+    private double sumActiveTime(IntBuffer buffer) {
+        double sum = 0;
+        boolean corrupted = false;
+        for (int j = 1; j <= mCores; j++) {
+            int time = buffer.get();
+            if (time < 0) {
+                // Even if error happens, we still need to continue reading.
+                // Buffer cannot be skipped.
+                Slog.e(TAG, "Negative time from active time proc: " + time);
+                corrupted = true;
+            } else {
+                sum += (double) time * 10 / j; // Unit is 10ms.
+            }
+        }
+        return corrupted ? -1 : sum;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
         synchronized (mProcReader) {
             final ByteBuffer bytes = mProcReader.readBytes();
             if (bytes == null || bytes.remaining() <= 4) {
@@ -89,6 +144,11 @@
             }
             final IntBuffer buf = bytes.asIntBuffer();
             final int cores = buf.get();
+            if (mCores != 0 && cores != mCores) {
+                Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores);
+                return;
+            }
+            mCores = cores;
             if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
                 Slog.wtf(TAG,
                         "Cpu active time format error: " + buf.remaining() + " / " + (cores
@@ -97,25 +157,7 @@
             }
             int numUids = buf.remaining() / (cores + 1);
             for (int i = 0; i < numUids; i++) {
-                int uid = buf.get();
-                boolean corrupted = false;
-                double curTime = 0;
-                for (int j = 1; j <= cores; j++) {
-                    int time = buf.get();
-                    if (time < 0) {
-                        Slog.e(TAG, "Corrupted data from active time proc: " + time);
-                        corrupted = true;
-                    } else {
-                        curTime += (double) time * 10 / j; // Unit is 10ms.
-                    }
-                }
-                double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
-                if (delta > 0 && !corrupted) {
-                    mLastUidCpuActiveTimeMs.put(uid, curTime);
-                    if (cb != null) {
-                        cb.onUidCpuActiveTime(uid, (long) delta);
-                    }
-                }
+                processUid.accept(buf);
             }
             if (DEBUG) {
                 Slog.d(TAG, "Read uids: " + numUids);
@@ -123,26 +165,11 @@
         }
     }
 
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidCpuActiveTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidCpuActiveTimeMs.keyAt(i);
-                cb.onUidCpuActiveTime(uid, mLastUidCpuActiveTimeMs.get(uid).longValue());
-            }
-        }
-    }
-
     public void removeUid(int uid) {
         mLastUidCpuActiveTimeMs.delete(uid);
     }
 
     public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
-            return;
-        }
         mLastUidCpuActiveTimeMs.put(startUid, null);
         mLastUidCpuActiveTimeMs.put(endUid, null);
         final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
index bf5b520..3cbfaea 100644
--- a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -24,6 +24,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
@@ -89,6 +90,72 @@
 
     @Override
     protected void readDeltaImpl(@Nullable Callback cb) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new double[mNumClusters];
+                mLastUidPolicyTimeMs.put(uid, lastTimes);
+            }
+            if (!sumClusterTime(buf, mCurTime)) {
+                return;
+            }
+            boolean valid = true;
+            boolean notify = false;
+            for (int i = 0; i < mNumClusters; i++) {
+                mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]);
+                if (mDeltaTime[i] < 0) {
+                    Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTime[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+                if (cb != null) {
+                    cb.onUidCpuPolicyTime(uid, mDeltaTime);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            if (sumClusterTime(buf, mCurTime)) {
+                for (int i = 0; i < mNumClusters; i++) {
+                    mCurTimeRounded[i] = (long) mCurTime[i];
+                }
+                callback.onUidCpuPolicyTime(uid, mCurTimeRounded);
+            }
+        });
+    }
+
+    private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) {
+        boolean valid = true;
+        for (int i = 0; i < mNumClusters; i++) {
+            clusterTime[i] = 0;
+            for (int j = 1; j <= mNumCoresOnCluster[i]; j++) {
+                int time = buffer.get();
+                if (time < 0) {
+                    Slog.e(TAG, "Negative time from cluster time proc: " + time);
+                    valid = false;
+                }
+                clusterTime[i] += (double) time * 10 / j; // Unit is 10ms.
+            }
+        }
+        return valid;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
         synchronized (mProcReader) {
             ByteBuffer bytes = mProcReader.readBytes();
             if (bytes == null || bytes.remaining() <= 4) {
@@ -130,7 +197,7 @@
             int numUids = buf.remaining() / (mNumCores + 1);
 
             for (int i = 0; i < numUids; i++) {
-                processUid(buf, cb);
+                processUid.accept(buf);
             }
             if (DEBUG) {
                 Slog.d(TAG, "Read uids: " + numUids);
@@ -138,57 +205,6 @@
         }
     }
 
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidPolicyTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidPolicyTimeMs.keyAt(i);
-                double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
-                for (int j = 0; j < mNumClusters; j++) {
-                    mCurTimeRounded[j] = (long) lastTimes[j];
-                }
-                cb.onUidCpuPolicyTime(uid, mCurTimeRounded);
-            }
-        }
-    }
-
-    private void processUid(IntBuffer buf, @Nullable Callback cb) {
-        int uid = buf.get();
-        double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
-        if (lastTimes == null) {
-            lastTimes = new double[mNumClusters];
-            mLastUidPolicyTimeMs.put(uid, lastTimes);
-        }
-
-        boolean notify = false;
-        boolean corrupted = false;
-
-        for (int j = 0; j < mNumClusters; j++) {
-            mCurTime[j] = 0;
-            for (int k = 1; k <= mNumCoresOnCluster[j]; k++) {
-                int time = buf.get();
-                if (time < 0) {
-                    Slog.e(TAG, "Corrupted data from cluster time proc uid: " + uid);
-                    corrupted = true;
-                }
-                mCurTime[j] += (double) time * 10 / k; // Unit is 10ms.
-            }
-            mDeltaTime[j] = (long) (mCurTime[j] - lastTimes[j]);
-            if (mDeltaTime[j] < 0) {
-                Slog.e(TAG, "Unexpected delta from cluster time proc uid: " + uid);
-                corrupted = true;
-            }
-            notify |= mDeltaTime[j] > 0;
-        }
-        if (notify && !corrupted) {
-            System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
-            if (cb != null) {
-                cb.onUidCpuPolicyTime(uid, mDeltaTime);
-            }
-        }
-    }
-
     // Returns if it has read valid info.
     private boolean readCoreInfo(IntBuffer buf, int numClusters) {
         int numCores = 0;
@@ -214,10 +230,6 @@
     }
 
     public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
-            return;
-        }
         mLastUidPolicyTimeMs.put(startUid, null);
         mLastUidPolicyTimeMs.put(endUid, null);
         final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index f65074f..5b46d0f 100644
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -21,11 +21,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.StrictMode;
-import android.os.SystemClock;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -34,6 +32,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads /proc/uid_time_in_state which has the format:
@@ -75,9 +74,6 @@
     private long[] mCurTimes; // Reuse to prevent GC.
     private long[] mDeltaTimes; // Reuse to prevent GC.
     private int mCpuFreqsCount;
-    private long mLastTimeReadMs = Long.MIN_VALUE;
-    private long mNowTimeMs;
-    private boolean mReadBinary = true;
     private final KernelCpuProcReader mProcReader;
 
     private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
@@ -140,180 +136,6 @@
         if (line == null) {
             return null;
         }
-        return readCpuFreqs(line, powerProfile);
-    }
-
-    public void setReadBinary(boolean readBinary) {
-        mReadBinary = readBinary;
-    }
-
-    @Override
-    protected void readDeltaImpl(@Nullable Callback callback) {
-        if (mCpuFreqs == null) {
-            return;
-        }
-        if (mReadBinary) {
-            readDeltaBinary(callback);
-        } else {
-            readDeltaString(callback);
-        }
-    }
-
-    private void readDeltaString(@Nullable Callback callback) {
-        mNowTimeMs = SystemClock.elapsedRealtime();
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
-            readDelta(reader, callback);
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
-        }
-        mLastTimeReadMs = mNowTimeMs;
-    }
-
-    @VisibleForTesting
-    public void readDeltaBinary(@Nullable Callback callback) {
-        synchronized (mProcReader) {
-            ByteBuffer bytes = mProcReader.readBytes();
-            if (bytes == null || bytes.remaining() <= 4) {
-                // Error already logged in mProcReader.
-                return;
-            }
-            if ((bytes.remaining() & 3) != 0) {
-                Slog.wtf(TAG, "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
-                return;
-            }
-            IntBuffer buf = bytes.asIntBuffer();
-            final int freqs = buf.get();
-            if (freqs != mCpuFreqsCount) {
-                Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
-                return;
-            }
-            if (buf.remaining() % (freqs + 1) != 0) {
-                Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
-                return;
-            }
-            int numUids = buf.remaining() / (freqs + 1);
-            for (int i = 0; i < numUids; i++) {
-                int uid = buf.get();
-                long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
-                if (lastTimes == null) {
-                    lastTimes = new long[mCpuFreqsCount];
-                    mLastUidCpuFreqTimeMs.put(uid, lastTimes);
-                }
-                boolean notify = false;
-                boolean corrupted = false;
-                for (int j = 0; j < freqs; j++) {
-                    mCurTimes[j] = (long) buf.get() * 10; // Unit is 10ms.
-                    mDeltaTimes[j] = mCurTimes[j] - lastTimes[j];
-                    if (mCurTimes[j] < 0 || mDeltaTimes[j] < 0) {
-                        Slog.e(TAG, "Unexpected data from freq time proc: " + mCurTimes[j]);
-                        corrupted = true;
-                    }
-                    notify |= mDeltaTimes[j] > 0;
-                }
-                if (notify && !corrupted) {
-                    System.arraycopy(mCurTimes, 0, lastTimes, 0, freqs);
-                    if (callback != null) {
-                        callback.onUidCpuFreqTime(uid, mDeltaTimes);
-                    }
-                }
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Read uids: " + numUids);
-            }
-        }
-    }
-
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidCpuFreqTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidCpuFreqTimeMs.keyAt(i);
-                cb.onUidCpuFreqTime(uid, mLastUidCpuFreqTimeMs.get(uid));
-            }
-        }
-    }
-
-    public void removeUid(int uid) {
-        mLastUidCpuFreqTimeMs.delete(uid);
-    }
-
-    public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            return;
-        }
-        mLastUidCpuFreqTimeMs.put(startUid, null);
-        mLastUidCpuFreqTimeMs.put(endUid, null);
-        final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
-        final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
-        mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
-    }
-
-    @VisibleForTesting
-    public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
-        String line = reader.readLine();
-        if (line == null) {
-            return;
-        }
-        while ((line = reader.readLine()) != null) {
-            final int index = line.indexOf(' ');
-            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
-            readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
-        }
-    }
-
-    private void readTimesForUid(int uid, String line, Callback callback) {
-        long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
-        if (uidTimeMs == null) {
-            uidTimeMs = new long[mCpuFreqsCount];
-            mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
-        }
-        final String[] timesStr = line.split(" ");
-        final int size = timesStr.length;
-        if (size != uidTimeMs.length) {
-            Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
-                    + " cpuFreqsCount: " + uidTimeMs.length);
-            return;
-        }
-        final long[] deltaUidTimeMs = new long[size];
-        final long[] curUidTimeMs = new long[size];
-        boolean notify = false;
-        for (int i = 0; i < size; ++i) {
-            // Times read will be in units of 10ms
-            final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
-            deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
-            // If there is malformed data for any uid, then we just log about it and ignore
-            // the data for that uid.
-            if (deltaUidTimeMs[i] < 0 || totalTimeMs < 0) {
-                if (DEBUG) {
-                    final StringBuilder sb = new StringBuilder("Malformed cpu freq data for UID=")
-                            .append(uid).append("\n");
-                    sb.append("data=").append("(").append(uidTimeMs[i]).append(",")
-                            .append(totalTimeMs).append(")").append("\n");
-                    sb.append("times=").append("(");
-                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
-                    sb.append(",");
-                    TimeUtils.formatDuration(mNowTimeMs, sb);
-                    sb.append(")");
-                    Slog.e(TAG, sb.toString());
-                }
-                return;
-            }
-            curUidTimeMs[i] = totalTimeMs;
-            notify = notify || (deltaUidTimeMs[i] > 0);
-        }
-        if (notify) {
-            System.arraycopy(curUidTimeMs, 0, uidTimeMs, 0, size);
-            if (callback != null) {
-                callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
-            }
-        }
-    }
-
-    private long[] readCpuFreqs(String line, PowerProfile powerProfile) {
         final String[] freqStr = line.split(" ");
         // First item would be "uid: " which needs to be ignored.
         mCpuFreqsCount = freqStr.length - 1;
@@ -339,10 +161,116 @@
             mPerClusterTimesAvailable = false;
         }
         Slog.i(TAG, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
-
         return mCpuFreqs;
     }
 
+    @Override
+    @VisibleForTesting
+    public void readDeltaImpl(@Nullable Callback callback) {
+        if (mCpuFreqs == null) {
+            return;
+        }
+        readImpl((buf) -> {
+            int uid = buf.get();
+            long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new long[mCpuFreqsCount];
+                mLastUidCpuFreqTimeMs.put(uid, lastTimes);
+            }
+            if (!getFreqTimeForUid(buf, mCurTimes)) {
+                return;
+            }
+            boolean notify = false;
+            boolean valid = true;
+            for (int i = 0; i < mCpuFreqsCount; i++) {
+                mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+                if (mDeltaTimes[i] < 0) {
+                    Slog.e(TAG, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTimes[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTimes, 0, lastTimes, 0, mCpuFreqsCount);
+                if (callback != null) {
+                    callback.onUidCpuFreqTime(uid, mDeltaTimes);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            if (getFreqTimeForUid(buf, mCurTimes)) {
+                callback.onUidCpuFreqTime(uid, mCurTimes);
+            }
+        });
+    }
+
+    private boolean getFreqTimeForUid(IntBuffer buffer, long[] freqTime) {
+        boolean valid = true;
+        for (int i = 0; i < mCpuFreqsCount; i++) {
+            freqTime[i] = (long) buffer.get() * 10; // Unit is 10ms.
+            if (freqTime[i] < 0) {
+                Slog.e(TAG, "Negative time from freq time proc: " + freqTime[i]);
+                valid = false;
+            }
+        }
+        return valid;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
+        synchronized (mProcReader) {
+            ByteBuffer bytes = mProcReader.readBytes();
+            if (bytes == null || bytes.remaining() <= 4) {
+                // Error already logged in mProcReader.
+                return;
+            }
+            if ((bytes.remaining() & 3) != 0) {
+                Slog.wtf(TAG, "Cannot parse freq time proc bytes to int: " + bytes.remaining());
+                return;
+            }
+            IntBuffer buf = bytes.asIntBuffer();
+            final int freqs = buf.get();
+            if (freqs != mCpuFreqsCount) {
+                Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
+                return;
+            }
+            if (buf.remaining() % (freqs + 1) != 0) {
+                Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
+                return;
+            }
+            int numUids = buf.remaining() / (freqs + 1);
+            for (int i = 0; i < numUids; i++) {
+                processUid.accept(buf);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Read uids: #" + numUids);
+            }
+        }
+    }
+
+    public void removeUid(int uid) {
+        mLastUidCpuFreqTimeMs.delete(uid);
+    }
+
+    public void removeUidsInRange(int startUid, int endUid) {
+        mLastUidCpuFreqTimeMs.put(startUid, null);
+        mLastUidCpuFreqTimeMs.put(endUid, null);
+        final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
+        final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
+        mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
     /**
      * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
      * read from the proc file.
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 4901080..f0e7796 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -115,6 +115,14 @@
         command.append(' ');
         command.append(appProcess);
 
+        // Generate bare minimum of debug information to be able to backtrace through JITed code.
+        // We assume that if the invoke wrapper is used, backtraces are desirable:
+        //  * The wrap.sh script can only be used by debuggable apps, which would enable this flag
+        //    without the script anyway (the fork-zygote path).  So this makes the two consistent.
+        //  * The wrap.* property can only be used on userdebug builds and is likely to be used by
+        //    developers (e.g. enable debug-malloc), in which case backtraces are also useful.
+        command.append(" -Xcompiler-option --generate-mini-debug-info");
+
         command.append(" /system/bin --application");
         if (niceName != null) {
             command.append(" '--nice-name=").append(niceName).append("'");
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 749d00c1..5b6b619 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -325,14 +325,14 @@
      * {@param stackBounds}.
      */
     public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
-            int imeHeight) {
+            int bottomOffset) {
         // Adjust the right/bottom to ensure the stack bounds never goes offscreen
         movementBoundsOut.set(insetBounds);
         movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right -
                 stackBounds.width());
         movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom -
                 stackBounds.height());
-        movementBoundsOut.bottom -= imeHeight;
+        movementBoundsOut.bottom -= bottomOffset;
     }
 
     /**
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 82ac241..d53090b 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import android.os.RemoteException;
+import android.util.ExceptionUtils;
 
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -36,21 +37,45 @@
     }
 
     /**
-     *
+     * Wraps a given {@code action} into one that ignores any {@link RemoteException}s
      */
     public static <T> Consumer<T> ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action) {
         return action;
     }
 
     /**
+     * Wraps the given {@link ThrowingRunnable} into one that handles any exceptions using the
+     * provided {@code handler}
+     */
+    public static Runnable handleExceptions(ThrowingRunnable r, Consumer<Throwable> handler) {
+        return () -> {
+            try {
+                r.run();
+            } catch (Throwable t) {
+                handler.accept(t);
+            }
+        };
+    }
+
+    /**
      * An equivalent of {@link Runnable} that allows throwing checked exceptions
      *
      * This can be used to specify a lambda argument without forcing all the checked exceptions
      * to be handled within it
      */
     @FunctionalInterface
-    public interface ThrowingRunnable {
+    @SuppressWarnings("FunctionalInterfaceMethodChanged")
+    public interface ThrowingRunnable extends Runnable {
         void runOrThrow() throws Exception;
+
+        @Override
+        default void run() {
+            try {
+                runOrThrow();
+            } catch (Exception ex) {
+                throw ExceptionUtils.propagate(ex);
+            }
+        }
     }
 
     /**
@@ -80,7 +105,7 @@
             try {
                 acceptOrThrow(t);
             } catch (Exception ex) {
-                throw new RuntimeException(ex);
+                throw ExceptionUtils.propagate(ex);
             }
         }
     }
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 933cc7a..577fa17 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -443,7 +443,7 @@
      */
     public static int resolveColor(Context context, int color) {
         if (color == Notification.COLOR_DEFAULT) {
-            return context.getColor(com.android.internal.R.color.notification_icon_default_color);
+            return context.getColor(com.android.internal.R.color.notification_default_color_light);
         }
         return color;
     }
@@ -475,20 +475,15 @@
             int backgroundColor, boolean isDark) {
         final int resolvedColor = resolveColor(context, notificationColor);
 
-        final int actionBg = context.getColor(
-                com.android.internal.R.color.notification_action_list);
-
         int color = resolvedColor;
-        color = NotificationColorUtil.ensureLargeTextContrast(color, actionBg, isDark);
         color = NotificationColorUtil.ensureTextContrast(color, backgroundColor, isDark);
 
         if (color != resolvedColor) {
             if (DEBUG){
                 Log.w(TAG, String.format(
-                        "Enhanced contrast of notification for %s %s (over action)"
+                        "Enhanced contrast of notification for %s"
                                 + " and %s (over background) by changing #%s to %s",
                         context.getPackageName(),
-                        NotificationColorUtil.contrastChange(resolvedColor, color, actionBg),
                         NotificationColorUtil.contrastChange(resolvedColor, color, backgroundColor),
                         Integer.toHexString(resolvedColor), Integer.toHexString(color)));
             }
@@ -552,6 +547,17 @@
         }
     }
 
+    public static int resolveDefaultColor(Context context, int backgroundColor) {
+        boolean useDark = shouldUseDark(backgroundColor);
+        if (useDark) {
+            return context.getColor(
+                    com.android.internal.R.color.notification_default_color_light);
+        } else {
+            return context.getColor(
+                    com.android.internal.R.color.notification_default_color_dark);
+        }
+    }
+
     public static int resolveActionBarColor(Context context, int backgroundColor) {
         if (backgroundColor == Notification.COLOR_DEFAULT) {
             return context.getColor(com.android.internal.R.color.notification_action_list);
diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java
index b962d4f..bf796cd 100644
--- a/core/java/com/android/internal/util/NotificationMessagingUtil.java
+++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java
@@ -26,7 +26,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 
 import java.util.Objects;
@@ -47,6 +46,18 @@
                 Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING), false, mSmsContentObserver);
     }
 
+    public boolean isImportantMessaging(StatusBarNotification sbn, int importance) {
+        if (importance < NotificationManager.IMPORTANCE_LOW) {
+            return false;
+        }
+
+        return hasMessagingStyle(sbn) || (isCategoryMessage(sbn) && isDefaultMessagingApp(sbn));
+    }
+
+    public boolean isMessaging(StatusBarNotification sbn) {
+        return hasMessagingStyle(sbn) || isDefaultMessagingApp(sbn) || isCategoryMessage(sbn);
+    }
+
     @SuppressWarnings("deprecation")
     private boolean isDefaultMessagingApp(StatusBarNotification sbn) {
         final int userId = sbn.getUserId();
@@ -73,25 +84,12 @@
         }
     };
 
-    public boolean isImportantMessaging(StatusBarNotification sbn, int importance) {
-        if (importance < NotificationManager.IMPORTANCE_LOW) {
-            return false;
-        }
-
-        return isMessaging(sbn);
+    private boolean hasMessagingStyle(StatusBarNotification sbn) {
+        Class<? extends Notification.Style> style = sbn.getNotification().getNotificationStyle();
+        return Notification.MessagingStyle.class.equals(style);
     }
 
-    public boolean isMessaging(StatusBarNotification sbn) {
-        Class<? extends Notification.Style> style = sbn.getNotification().getNotificationStyle();
-        if (Notification.MessagingStyle.class.equals(style)) {
-            return true;
-        }
-
-        if (Notification.CATEGORY_MESSAGE.equals(sbn.getNotification().category)
-                && isDefaultMessagingApp(sbn)) {
-            return true;
-        }
-
-        return false;
+    private boolean isCategoryMessage(StatusBarNotification sbn) {
+        return Notification.CATEGORY_MESSAGE.equals(sbn.getNotification().category);
     }
 }
diff --git a/core/java/com/android/internal/util/ProviderAccessStats.java b/core/java/com/android/internal/util/ProviderAccessStats.java
new file mode 100644
index 0000000..c3ffb02
--- /dev/null
+++ b/core/java/com/android/internal/util/ProviderAccessStats.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util;
+
+import android.os.SystemClock;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import java.io.PrintWriter;
+
+public class ProviderAccessStats {
+    private final Object mLock = new Object();
+
+    private final long mStartUptime = SystemClock.uptimeMillis();
+
+    private final SparseBooleanArray mAllCallingUids = new SparseBooleanArray();
+    private final SparseLongArray mQueryStats = new SparseLongArray(16);
+    private final SparseLongArray mBatchStats = new SparseLongArray(0);
+    private final SparseLongArray mInsertStats = new SparseLongArray(0);
+    private final SparseLongArray mUpdateStats = new SparseLongArray(0);
+    private final SparseLongArray mDeleteStats = new SparseLongArray(0);
+    private final SparseLongArray mInsertInBatchStats = new SparseLongArray(0);
+    private final SparseLongArray mUpdateInBatchStats = new SparseLongArray(0);
+    private final SparseLongArray mDeleteInBatchStats = new SparseLongArray(0);
+
+    private final SparseLongArray mOperationDurationMillis = new SparseLongArray(16);
+
+    private static class PerThreadData {
+        public int nestCount;
+        public long startUptimeMillis;
+    }
+
+    private final ThreadLocal<PerThreadData> mThreadLocal =
+            ThreadLocal.withInitial(() -> new PerThreadData());
+
+    private void incrementStats(int callingUid, SparseLongArray stats) {
+        synchronized (mLock) {
+            stats.put(callingUid, stats.get(callingUid) + 1);
+            mAllCallingUids.put(callingUid, true);
+        }
+
+        final PerThreadData data = mThreadLocal.get();
+        data.nestCount++;
+        if (data.nestCount == 1) {
+            data.startUptimeMillis = SystemClock.uptimeMillis();
+        }
+    }
+
+    private void incrementStats(int callingUid, boolean inBatch,
+            SparseLongArray statsNonBatch, SparseLongArray statsInBatch) {
+        incrementStats(callingUid, inBatch ? statsInBatch : statsNonBatch);
+    }
+
+    public final void incrementInsertStats(int callingUid, boolean inBatch) {
+        incrementStats(callingUid, inBatch, mInsertStats, mInsertInBatchStats);
+    }
+
+    public final void incrementUpdateStats(int callingUid, boolean inBatch) {
+        incrementStats(callingUid, inBatch, mUpdateStats, mUpdateInBatchStats);
+    }
+
+    public final void incrementDeleteStats(int callingUid, boolean inBatch) {
+        incrementStats(callingUid, inBatch, mDeleteStats, mDeleteInBatchStats);
+    }
+
+    public final void incrementQueryStats(int callingUid) {
+        incrementStats(callingUid, mQueryStats);
+    }
+
+    public final void incrementBatchStats(int callingUid) {
+        incrementStats(callingUid, mBatchStats);
+    }
+
+    public void finishOperation(int callingUid) {
+        final PerThreadData data = mThreadLocal.get();
+        data.nestCount--;
+        if (data.nestCount == 0) {
+            // Because we only have millisecond granularity, let's always attribute at least 1ms
+            // for each operation.
+            final long duration = Math.max(1, SystemClock.uptimeMillis() - data.startUptimeMillis);
+
+            synchronized (mLock) {
+                mOperationDurationMillis.put(callingUid,
+                        mOperationDurationMillis.get(callingUid) + duration);
+            }
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            pw.print("  Process uptime: ");
+            pw.print((SystemClock.uptimeMillis() - mStartUptime) / (60 * 1000));
+            pw.println(" minutes");
+            pw.println();
+
+            pw.print(prefix);
+            pw.println("Client activities:");
+            pw.print(prefix);
+            pw.println("  UID        Query  Insert Update Delete   Batch Insert Update Delete"
+                    + "          Sec");
+            for (int i = 0; i < mAllCallingUids.size(); i++) {
+                final int uid = mAllCallingUids.keyAt(i);
+                pw.print(prefix);
+                pw.println(String.format(
+                        "  %-9d %6d  %6d %6d %6d  %6d %6d %6d %6d %12.3f",
+                        uid,
+                        mQueryStats.get(uid),
+                        mInsertStats.get(uid),
+                        mUpdateStats.get(uid),
+                        mDeleteStats.get(uid),
+                        mBatchStats.get(uid),
+                        mInsertInBatchStats.get(uid),
+                        mUpdateInBatchStats.get(uid),
+                        mDeleteInBatchStats.get(uid),
+                        (mOperationDurationMillis.get(uid) / 1000.0)
+                ));
+            }
+            pw.println();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 25e1589..bff34ca 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -74,14 +74,17 @@
     void setRecoverySecretTypes(in int[] secretTypes);
     int[] getRecoverySecretTypes();
     int[] getPendingRecoverySecretTypes();
-    void recoverySecretAvailable(in KeyChainProtectionParams recoverySecret);
     byte[] startRecoverySession(in String sessionId,
             in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
             in List<KeyChainProtectionParams> secrets);
-    byte[] startRecoverySessionWithCertPath(in String sessionId,
+    byte[] startRecoverySessionWithCertPath(in String sessionId, in String rootCertificateAlias,
             in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge,
             in List<KeyChainProtectionParams> secrets);
     Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
+    Map/*<String, String>*/ recoverKeyChainSnapshot(
+            in String sessionId,
+            in byte[] recoveryKeyBlob,
+            in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index fde7e96..d4ab426 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.widget;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
@@ -59,7 +68,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-
 /**
  * Utilities for the lock pattern and its settings.
  */
@@ -585,7 +593,7 @@
             return quality;
         }
 
-        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+        return PASSWORD_QUALITY_UNSPECIFIED;
     }
 
     /**
@@ -604,13 +612,16 @@
      * Clear any lock pattern or password.
      */
     public void clearLock(String savedCredential, int userHandle) {
-        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
 
         try{
             getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
-                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
-        } catch (RemoteException e) {
-            // well, we tried...
+                    PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to clear lock", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            return;
         }
 
         if (userHandle == UserHandle.USER_SYSTEM) {
@@ -669,32 +680,34 @@
      * @param userId the user whose pattern is to be saved.
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
-        try {
-            if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
-                throw new IllegalArgumentException("pattern must not be null and at least "
-                        + MIN_LOCK_PATTERN_SIZE + " dots long.");
-            }
-
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-            getLockSettings().setLockCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN,
-                    savedPattern, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-
-            // Update the device encryption password.
-            if (userId == UserHandle.USER_SYSTEM
-                    && LockPatternUtils.isDeviceEncryptionEnabled()) {
-                if (!shouldEncryptWithCredentials(true)) {
-                    clearEncryptionPassword();
-                } else {
-                    String stringPattern = patternToString(pattern);
-                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
-                }
-            }
-
-            reportPatternWasChosen(userId);
-            onAfterChangingPassword(userId);
-        } catch (RemoteException re) {
-            Log.e(TAG, "Couldn't save lock pattern " + re);
+        if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
+            throw new IllegalArgumentException("pattern must not be null and at least "
+                    + MIN_LOCK_PATTERN_SIZE + " dots long.");
         }
+
+        final String stringPattern = patternToString(pattern);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userId);
+        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
+        try {
+            getLockSettings().setLockCredential(stringPattern, CREDENTIAL_TYPE_PATTERN,
+                    savedPattern, PASSWORD_QUALITY_SOMETHING, userId);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't save lock pattern", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userId);
+            return;
+        }
+        // Update the device encryption password.
+        if (userId == UserHandle.USER_SYSTEM
+                && LockPatternUtils.isDeviceEncryptionEnabled()) {
+            if (!shouldEncryptWithCredentials(true)) {
+                clearEncryptionPassword();
+            } else {
+                updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+            }
+        }
+
+        reportPatternWasChosen(userId);
+        onAfterChangingPassword(userId);
     }
 
     private void updateCryptoUserInfo(int userId) {
@@ -796,25 +809,27 @@
      */
     public void saveLockPassword(String password, String savedPassword, int requestedQuality,
             int userHandle) {
-        try {
-            if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
-                throw new IllegalArgumentException("password must not be null and at least "
-                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
-            }
-
-            setLong(PASSWORD_TYPE_KEY,
-                    computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
-                    userHandle);
-            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
-                    requestedQuality, userHandle);
-
-            updateEncryptionPasswordIfNeeded(password,
-                    PasswordMetrics.computeForPassword(password).quality, userHandle);
-            updatePasswordHistory(password, userHandle);
-        } catch (RemoteException re) {
-            // Cant do much
-            Log.e(TAG, "Unable to save lock password " + re);
+        if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
+            throw new IllegalArgumentException("password must not be null and at least "
+                    + "of length " + MIN_LOCK_PASSWORD_SIZE);
         }
+
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(
+                computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
+                userHandle);
+        try {
+            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD,
+                    savedPassword, requestedQuality, userHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to save lock password", e);
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            return;
+        }
+
+        updateEncryptionPasswordIfNeeded(password,
+                PasswordMetrics.computeForPassword(password).quality, userHandle);
+        updatePasswordHistory(password, userHandle);
     }
 
     /**
@@ -828,9 +843,8 @@
             if (!shouldEncryptWithCredentials(true)) {
                 clearEncryptionPassword();
             } else {
-                boolean numeric = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-                boolean numericComplex = quality
-                        == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+                boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
+                boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
                 int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
                         : StorageManager.CRYPT_TYPE_PASSWORD;
                 updateEncryptionPassword(type, password);
@@ -894,8 +908,11 @@
      * @return stored password quality
      */
     public int getKeyguardStoredPasswordQuality(int userHandle) {
-        return (int) getLong(PASSWORD_TYPE_KEY,
-                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        return (int) getLong(PASSWORD_TYPE_KEY, PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+    }
+
+    private void setKeyguardStoredPasswordQuality(int quality, int userHandle) {
+        setLong(PASSWORD_TYPE_KEY, quality, userHandle);
     }
 
     /**
@@ -909,9 +926,9 @@
             int computedQuality = PasswordMetrics.computeForPassword(credential).quality;
             quality = Math.max(requestedQuality, computedQuality);
         } else if (type == CREDENTIAL_TYPE_PATTERN)  {
-            quality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+            quality = PASSWORD_QUALITY_SOMETHING;
         } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
-            quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            quality = PASSWORD_QUALITY_UNSPECIFIED;
         }
         return quality;
     }
@@ -1125,12 +1142,12 @@
     }
 
     private boolean isLockPasswordEnabled(int mode, int userId) {
-        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
+        final boolean passwordEnabled = mode == PASSWORD_QUALITY_ALPHABETIC
+                || mode == PASSWORD_QUALITY_NUMERIC
+                || mode == PASSWORD_QUALITY_NUMERIC_COMPLEX
+                || mode == PASSWORD_QUALITY_ALPHANUMERIC
+                || mode == PASSWORD_QUALITY_COMPLEX
+                || mode == PASSWORD_QUALITY_MANAGED;
         return passwordEnabled && savedPasswordExists(userId);
     }
 
@@ -1155,8 +1172,7 @@
     }
 
     private boolean isLockPatternEnabled(int mode, int userId) {
-        return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
-                && savedPatternExists(userId);
+        return mode == PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId);
     }
 
     /**
@@ -1551,7 +1567,7 @@
                     token, quality, userId)) {
                 return false;
             }
-            setLong(PASSWORD_TYPE_KEY, quality, userId);
+            setKeyguardStoredPasswordQuality(quality, userId);
 
             updateEncryptionPasswordIfNeeded(credential, quality, userId);
             updatePasswordHistory(credential, userId);
@@ -1560,12 +1576,10 @@
                 throw new IllegalArgumentException("password must be emtpy for NONE type");
             }
             if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE,
-                    tokenHandle, token, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                    userId)) {
+                    tokenHandle, token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
                 return false;
             }
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                    userId);
+            setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
 
             if (userId == UserHandle.USER_SYSTEM) {
                 // Set the encryption password to default.
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index d6496cd..7166c75 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -42,7 +42,6 @@
     }
 
     auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
-    auto info = imageDecoder->mCodec->getInfo();
     const SkISize scaledSize = SkISize::Make(width, height);
     SkIRect subset;
     if (jsubset) {
@@ -51,6 +50,35 @@
         subset = SkIRect::MakeWH(width, height);
     }
 
+    auto info = imageDecoder->mCodec->getInfo();
+    bool hasRestoreFrame = false;
+    if (imageDecoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
+        if (width < info.width() && height < info.height()) {
+            // WebP will scale its SkBitmap to the scaled size.
+            // FIXME: b/73529447 GIF should do the same.
+            info = info.makeWH(width, height);
+        }
+    } else {
+        const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
+        for (int i = 0; i < frameCount; ++i) {
+            SkCodec::FrameInfo frameInfo;
+            if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
+                doThrowIOE(env, "Failed to read frame info!");
+                return 0;
+            }
+            if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
+                hasRestoreFrame = true;
+                break;
+            }
+        }
+    }
+
+    size_t bytesUsed = info.computeMinByteSize();
+    // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
+    // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
+    // frame and the next frame. (The former assumes that the image is animated, and the
+    // latter assumes that it is drawn to a hardware canvas.)
+    bytesUsed *= hasRestoreFrame ? 4 : 3;
     sk_sp<SkPicture> picture;
     if (jpostProcess) {
         SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
@@ -63,6 +91,7 @@
             return 0;
         }
         picture = recorder.finishRecordingAsPicture();
+        bytesUsed += picture->approximateBytesUsed();
     }
 
 
@@ -74,7 +103,10 @@
         return 0;
     }
 
-    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(animatedImg));
+    bytesUsed += sizeof(animatedImg.get());
+
+    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
+                                                                    bytesUsed));
     return reinterpret_cast<jlong>(drawable.release());
 }
 
@@ -202,10 +234,9 @@
     }
 }
 
-static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    // FIXME: Report the size of the internal SkBitmap etc.
-    return sizeof(drawable);
+    return drawable->byteSize();
 }
 
 static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index ce4e384..5a74a24 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1269,6 +1269,15 @@
     return GraphicsJNI::isColorSpaceSRGB(colorSpace);
 }
 
+static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return JNI_FALSE;
+
+    SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+    sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear();
+    return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE;
+}
+
 static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle,
         jfloatArray xyzArray, jfloatArray paramsArray) {
 
@@ -1614,6 +1623,7 @@
         (void*) Bitmap_createGraphicBufferHandle },
     {   "nativeGetColorSpace",      "(J[F[F)Z", (void*)Bitmap_getColorSpace },
     {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
+    {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
     {   "nativeCopyColorSpace",     "(JJ)V",
         (void*)Bitmap_copyColorSpace },
 };
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 4b6578b..3fcedd0 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -30,8 +30,8 @@
 
 class SkColorFilterGlue {
 public:
-    static void SafeUnref(SkShader* shader) {
-        SkSafeUnref(shader);
+    static void SafeUnref(SkColorFilter* filter) {
+        SkSafeUnref(filter);
     }
 
     static jlong GetNativeFinalizer(JNIEnv*, jobject) {
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index e2ce1a4..726c450 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -38,48 +38,81 @@
 
 static jclass    gImageDecoder_class;
 static jclass    gSize_class;
-static jclass    gIncomplete_class;
+static jclass    gDecodeException_class;
 static jclass    gCanvas_class;
 static jmethodID gImageDecoder_constructorMethodID;
 static jmethodID gImageDecoder_postProcessMethodID;
 static jmethodID gSize_constructorMethodID;
-static jmethodID gIncomplete_constructorMethodID;
+static jmethodID gDecodeException_constructorMethodID;
 static jmethodID gCallback_onPartialImageMethodID;
 static jmethodID gCanvas_constructorMethodID;
 static jmethodID gCanvas_releaseMethodID;
 
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
+// Clear and return any pending exception for handling other than throwing directly.
+static jthrowable get_and_clear_exception(JNIEnv* env) {
+    jthrowable jexception = env->ExceptionOccurred();
+    if (jexception) {
+        env->ExceptionClear();
+    }
+    return jexception;
+}
+
+// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
+static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const char* msg,
+                               jthrowable cause, jobject source) {
+    jstring jstr = nullptr;
+    if (msg) {
+        jstr = env->NewStringUTF(msg);
+        if (!jstr) {
+            // Out of memory.
+            return nullptr;
+        }
+    }
+    jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class,
+            gDecodeException_constructorMethodID, error, jstr, cause, source);
+    // Only throw if not out of memory.
+    if (exception) {
+        env->Throw(exception);
+    }
+    return nullptr;
+}
+
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
     if (!stream.get()) {
-        doThrowIOE(env, "Failed to create a stream");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+                               nullptr, source);
     }
     std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
     SkCodec::Result result;
     auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+    if (jthrowable jexception = get_and_clear_exception(env)) {
+        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+    }
     if (!codec) {
         switch (result) {
             case SkCodec::kIncompleteInput:
-                env->ThrowNew(gIncomplete_class, "Incomplete input");
-                break;
+                return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source);
             default:
                 SkString msg;
                 msg.printf("Failed to create image decoder with message '%s'",
                            SkCodec::ResultToString(result));
-                doThrowIOE(env, msg.c_str());
-                break;
+                return throw_exception(env, ImageDecoder::kSourceMalformedData,  msg.c_str(),
+                                       nullptr, source);
 
         }
-        return nullptr;
     }
 
-    // FIXME: Avoid parsing the whole image?
     const bool animated = codec->getFrameCount() > 1;
+    if (jthrowable jexception = get_and_clear_exception(env)) {
+        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+    }
+
     decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
             SkAndroidCodec::ExifOrientationBehavior::kRespect);
     if (!decoder->mCodec.get()) {
-        doThrowIOE(env, "Could not create AndroidCodec");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData, "", nullptr, source);
     }
+
     const auto& info = decoder->mCodec->getInfo();
     const int width = info.width();
     const int height = info.height();
@@ -89,26 +122,26 @@
 }
 
 static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
-        jobject fileDescriptor) {
+        jobject fileDescriptor, jobject source) {
     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     struct stat fdStat;
     if (fstat(descriptor, &fdStat) == -1) {
-        doThrowIOE(env, "broken file descriptor; fstat returned -1");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData,
+                               "broken file descriptor; fstat returned -1", nullptr, source);
     }
 
     int dupDescriptor = dup(descriptor);
     FILE* file = fdopen(dupDescriptor, "r");
     if (file == NULL) {
         close(dupDescriptor);
-        doThrowIOE(env, "Could not open file");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file",
+                               nullptr, source);
     }
     std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
 
     if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
-        return native_create(env, std::move(fileStream));
+        return native_create(env, std::move(fileStream), source);
     }
 
     // FIXME: This allows us to pretend the current location is the beginning,
@@ -116,44 +149,46 @@
     // point as the beginning.
     std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream),
                 SkCodec::MinBufferedBytesNeeded()));
-    return native_create(env, std::move(stream));
+    return native_create(env, std::move(stream), source);
 }
 
 static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
-        jobject is, jbyteArray storage) {
+        jobject is, jbyteArray storage, jobject source) {
     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
 
     if (!stream.get()) {
-        doThrowIOE(env, "Failed to create stream!");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+                               nullptr, source);
     }
+
     std::unique_ptr<SkStream> bufferedStream(
         SkFrontBufferedStream::Make(std::move(stream),
         SkCodec::MinBufferedBytesNeeded()));
-    return native_create(env, std::move(bufferedStream));
+    return native_create(env, std::move(bufferedStream), source);
 }
 
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
+                                         jobject source) {
     Asset* asset = reinterpret_cast<Asset*>(assetPtr);
     std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
-    return native_create(env, std::move(stream));
+    return native_create(env, std::move(stream), source);
 }
 
 static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
-                                              jint initialPosition, jint limit) {
+                                              jint initialPosition, jint limit, jobject source) {
     std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
                                                                      initialPosition, limit);
     if (!stream) {
-        doThrowIOE(env, "Failed to read ByteBuffer");
-        return nullptr;
+        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
+                               nullptr, source);
     }
-    return native_create(env, std::move(stream));
+    return native_create(env, std::move(stream), source);
 }
 
 static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
-                                             jint offset, jint length) {
+                                             jint offset, jint length, jobject source) {
     std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
-    return native_create(env, std::move(stream));
+    return native_create(env, std::move(stream), source);
 }
 
 jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -170,10 +205,8 @@
     return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
 }
 
-// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
-// otherwise.
 static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
-                                          jobject jcallback, jobject jpostProcess,
+                                          jobject jdecoder, jboolean jpostProcess,
                                           jint desiredWidth, jint desiredHeight, jobject jsubset,
                                           jboolean requireMutable, jint allocator,
                                           jboolean requireUnpremul, jboolean preferRamOverQuality,
@@ -264,11 +297,8 @@
     SkAndroidCodec::AndroidOptions options;
     options.fSampleSize = sampleSize;
     auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
-    jthrowable jexception = env->ExceptionOccurred();
-    if (jexception) {
-        env->ExceptionClear();
-    }
-    int onPartialImageError = jexception ? 1  // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+    jthrowable jexception = get_and_clear_exception(env);
+    int onPartialImageError = jexception ? ImageDecoder::kSourceException
                                          : 0; // No error.
     switch (result) {
         case SkCodec::kSuccess:
@@ -278,12 +308,12 @@
             break;
         case SkCodec::kIncompleteInput:
             if (!jexception) {
-                onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+                onPartialImageError = ImageDecoder::kSourceIncomplete;
             }
             break;
         case SkCodec::kErrorInInput:
             if (!jexception) {
-                onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR
+                onPartialImageError = ImageDecoder::kSourceMalformedData;
             }
             break;
         default:
@@ -293,24 +323,12 @@
             return nullptr;
     }
 
-    if (jexception || onPartialImageError) {
-        bool throwException = !jcallback ||
-            !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
-                                    onPartialImageError);
+    if (onPartialImageError) {
+        env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError,
+                jexception);
         if (env->ExceptionCheck()) {
             return nullptr;
         }
-
-        if (throwException) {
-            if (jexception) {
-                env->Throw(jexception);
-            } else if (onPartialImageError == 2) {
-                env->ThrowNew(gIncomplete_class, "Incomplete input");
-            } else {
-                doThrowIOE(env, "image has an error!");
-            }
-            return nullptr;
-        }
     }
 
     float scaleX = 1.0f;
@@ -357,11 +375,6 @@
             SkIRect subset;
             GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
 
-            // FIXME: If there is no scale, should this instead call
-            // SkBitmap::extractSubset? If we could upload a subset
-            // (b/70626068), this would save memory and time. Even for a
-            // software Bitmap, the extra speed might be worth the memory
-            // tradeoff if the subset is large?
             translateX    = -subset.fLeft;
             translateY    = -subset.fTop;
             desiredWidth  =  subset.width();
@@ -404,7 +417,7 @@
     if (jpostProcess) {
         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
 
-        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas));
+        jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas));
         if (env->ExceptionCheck()) {
             return nullptr;
         }
@@ -495,12 +508,12 @@
 }
 
 static const JNINativeMethod gImageDecoderMethods[] = {
-    { "nCreate",        "(J)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
-    { "nCreate",        "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
-    { "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;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+    { "nCreate",        "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
+    { "nCreate",        "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+    { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+    { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+    { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
@@ -516,10 +529,10 @@
     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");
+    gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException"));
+    gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V");
 
-    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(I)Z");
+    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V");
 
     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 5d7e676..fd9827b 100644
--- a/core/jni/android/graphics/ImageDecoder.h
+++ b/core/jni/android/graphics/ImageDecoder.h
@@ -33,6 +33,13 @@
         kHardware_Allocator     = 3,
     };
 
+    // These need to stay in sync with ImageDecoder.java's Error constants.
+    enum Error {
+        kSourceException     = 1,
+        kSourceIncomplete    = 2,
+        kSourceMalformedData = 3,
+    };
+
     // These need to stay in sync with PixelFormat.java's Format constants.
     enum PixelFormat {
         kUnknown     =  0,
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index d20c7ef..32ac30f 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -92,20 +92,7 @@
         renderFlags |= FPDF_PRINTING;
     }
 
-    // PDF's coordinate system origin is left-bottom while in graphics it
-    // is the top-left. So, translate the PDF coordinates to ours.
-    SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1);
-    SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page));
-    SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX);
-
-    // Apply the transformation
-    SkMatrix matrix;
-    if (transformPtr == 0) {
-        matrix = coordinateChange;
-    } else {
-        matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), coordinateChange);
-    }
-
+    SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr);
     SkScalar transformValues[6];
     if (!matrix.asAffine(transformValues)) {
         jniThrowException(env, "java/lang/IllegalArgumentException",
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 0017f6c..c9bfa13 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -18,6 +18,7 @@
 #include "GraphicsJNI.h"
 #include "core_jni_helpers.h"
 
+#include <android/api-level.h>
 #include <androidfw/ResourceTypes.h>
 #include <hwui/Canvas.h>
 #include <hwui/Paint.h>
@@ -467,6 +468,13 @@
 static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
                            jint meshWidth, jint meshHeight, jfloatArray jverts,
                            jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+    if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
+        // Before P we forgot to respect these. Now that we do respect them, explicitly
+        // zero them for backward compatibility.
+        vertIndex = 0;
+        colorIndex = 0;
+    }
+
     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
     AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
     AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
@@ -474,7 +482,8 @@
     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
     Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
     get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
-                                             vertA.ptr(), colorA.ptr(), paint);
+                                             vertA.ptr() + vertIndex*2,
+                                             colorA.ptr() + colorIndex, paint);
 }
 
 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index a94cac0..9ec7517 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -124,6 +124,12 @@
         if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) {
             //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks);
             env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks);
+            // There should not be any exceptions. But we must guarantee
+            // there are none on return.
+            if (env->ExceptionCheck()) {
+                env->ExceptionClear();
+                LOG(ERROR) << "Exception pending after sysprop_change!";
+            }
         }
     }
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a30b2ad..04cb08f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -116,9 +116,13 @@
     ScopedUtfChars name(env, nameStr);
     sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
     SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
-    sp<SurfaceControl> surface = client->createSurface(
-            String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);
-    if (surface == NULL) {
+    sp<SurfaceControl> surface;
+    status_t err = client->createSurfaceChecked(
+            String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
+    if (err == NAME_NOT_FOUND) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return 0;
+    } else if (err != NO_ERROR) {
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
     }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index b614c89..451f278 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -988,6 +988,11 @@
     Properties::debuggingEnabled = enable;
 }
 
+static void android_view_ThreadedRenderer_setIsolatedProcess(JNIEnv*, jclass, jboolean isolated) {
+    Properties::isolatedProcess = isolated;
+}
+
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1097,6 +1102,7 @@
     { "nHackySetRTAnimationsEnabled", "(Z)V",
             (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
     { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
+    { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7c8a52d..b5fd792 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -666,7 +666,9 @@
       }
     }
 
-    if (!is_system_server) {
+    // If this zygote isn't root, it won't be able to create a process group,
+    // since the directory is owned by root.
+    if (!is_system_server && getuid() == 0) {
         int rc = createProcessGroup(uid, getpid());
         if (rc != 0) {
             if (rc == -EROFS) {
diff --git a/core/proto/android/app/jobparameters.proto b/core/proto/android/app/job/enums.proto
similarity index 66%
rename from core/proto/android/app/jobparameters.proto
rename to core/proto/android/app/job/enums.proto
index 4f6a2a2..0f14f20 100644
--- a/core/proto/android/app/jobparameters.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -15,19 +15,18 @@
  */
 
 syntax = "proto2";
+
+package android.app.job;
+
+option java_outer_classname = "JobProtoEnums";
 option java_multiple_files = true;
 
-package android.app;
-
-/**
- * An android.app.JobParameters object.
- */
-message JobParametersProto {
-    enum CancelReason {
-        REASON_CANCELLED = 0;
-        REASON_CONSTRAINTS_NOT_SATISFIED = 1;
-        REASON_PREEMPT = 2;
-        REASON_TIMEOUT = 3;
-        REASON_DEVICE_IDLE = 4;
-    }
+// Reasons a job is stopped.
+// Primarily used in android.app.job.JobParameters.java.
+enum StopReasonEnum {
+  STOP_REASON_CANCELLED = 0;
+  STOP_REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+  STOP_REASON_PREEMPT = 2;
+  STOP_REASON_TIMEOUT = 3;
+  STOP_REASON_DEVICE_IDLE = 4;
 }
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index f468143..8e98ac9 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -19,7 +19,7 @@
 
 package android.os;
 
-import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/app/job/enums.proto";
 import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -208,32 +208,12 @@
 
   message DataConnection {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    enum Name {
-      NONE = 0;
-      GPRS = 1;
-      EDGE = 2;
-      UMTS = 3;
-      CDMA = 4;
-      EVDO_0 = 5;
-      EVDO_A = 6;
-      ONE_X_RTT = 7; // 1xRTT.
-      HSDPA = 8;
-      HSUPA = 9;
-      HSPA = 10;
-      IDEN = 11;
-      EVDO_B = 12;
-      LTE = 13;
-      EHRPD = 14;
-      HSPAP = 15;
-      GSM = 16;
-      TD_SCDMA = 17;
-      IWLAN = 18;
-      LTE_CA = 19;
-      OTHER = 20;
-    };
-    optional Name name = 1;
-    optional TimerProto total = 2;
+    oneof type {
+      android.telephony.NetworkTypeEnum name = 1;
+      // If is_none is not set, then the name is a valid network type.
+      bool is_none = 2;
+    }
+    optional TimerProto total = 3;
   };
   repeated DataConnection data_connection = 8;
 
@@ -657,7 +637,7 @@
     message ReasonCount {
       option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-      optional android.app.JobParametersProto.CancelReason name = 1;
+      optional android.app.job.StopReasonEnum name = 1;
       optional int32 count = 2;
     }
     repeated ReasonCount reason_count = 2;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 4657dc4..476d5fe 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -118,17 +118,17 @@
 
     // Stack dumps
     optional android.os.BackTraceProto native_traces = 1200 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "native"
     ];
 
     optional android.os.BackTraceProto hal_traces = 1201 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "hal"
     ];
 
     optional android.os.BackTraceProto java_traces = 1202 [
-        (section).type = SECTION_TOMBSTONE,
+        (section).type = SECTION_NONE,
         (section).args = "java"
     ];
 
@@ -169,7 +169,7 @@
     ];
 
     optional GZippedFileProto last_kmsg = 2007 [
-        (section).type = SECTION_GZIP,
+        (section).type = SECTION_NONE, // disable until selinux permission is gained
         (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg",
         (privacy).dest = DEST_AUTOMATIC
     ];
@@ -231,22 +231,22 @@
         (section).args = "procstats --proto"
     ];
 
-    optional com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto activities = 3012 [
+    optional com.android.server.am.ActivityManagerServiceDumpActivitiesProto activities = 3012 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "activity --proto activities"
     ];
 
-    optional com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto broadcasts = 3013 [
+    optional com.android.server.am.ActivityManagerServiceDumpBroadcastsProto broadcasts = 3013 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "activity --proto broadcasts"
     ];
 
-    optional com.android.server.am.proto.ActivityManagerServiceDumpServicesProto amservices = 3014 [
+    optional com.android.server.am.ActivityManagerServiceDumpServicesProto amservices = 3014 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "activity --proto service"
     ];
 
-    optional com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto amprocesses = 3015 [
+    optional com.android.server.am.ActivityManagerServiceDumpProcessesProto amprocesses = 3015 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "activity --proto processes"
     ];
@@ -256,12 +256,12 @@
         (section).args = "alarm --proto"
     ];
 
-    optional com.android.server.wm.proto.WindowManagerServiceDumpProto window = 3017 [
+    optional com.android.server.wm.WindowManagerServiceDumpProto window = 3017 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "window --proto"
     ];
 
-    optional com.android.server.am.proto.MemInfoDumpProto meminfo = 3018 [
+    optional com.android.server.am.MemInfoDumpProto meminfo = 3018 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "meminfo -a --proto"
     ];
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 53e6532..89665db 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -228,6 +228,7 @@
     optional SettingProto ble_scan_low_power_interval_ms = 161 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto ble_scan_balanced_interval_ms = 162 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto ble_scan_low_latency_interval_ms = 163 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto ble_scan_background_mode = 389 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto wifi_saved_state = 164 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto wifi_supplicant_scan_interval_ms = 165 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto wifi_enhanced_auto_join = 166 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -466,8 +467,10 @@
     // skipped.
     optional SettingProto override_settings_provider_restore_any_version = 355 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto chained_battery_attribution_enabled = 356 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_compat_allowed_packages = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto autofill_compat_mode_allowed_packages = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto hidden_api_blacklist_exemptions = 358 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto sound_trigger_detection_service_op_timeout = 387  [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto max_sound_trigger_detection_service_ops_per_day = 388  [ (android.privacy).dest = DEST_AUTOMATIC ];
     // Subscription to be used for voice call on a multi sim device. The
     // supported values are 0 = SUB1, 1 = SUB2 and etc.
     optional SettingProto multi_sim_voice_call_subscription = 359 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -505,7 +508,7 @@
     optional SettingsProto backup_agent_timeout_parameters = 386;
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 387;
+    // Next tag = 390;
 }
 
 message SecureSettingsProto {
@@ -742,9 +745,10 @@
     optional SettingProto backup_manager_constants = 193;
     optional SettingProto backup_local_transport_parameters = 194;
     optional SettingProto bluetooth_on_while_driving = 195 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingsProto volume_hush_gesture = 196 [ (android.privacy).dest = DEST_AUTOMATIC ];
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 196
+    // Next tag = 197
 }
 
 message SystemSettingsProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 5491ca5..0c617ff 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -16,7 +16,7 @@
 
 syntax = "proto2";
 
-package com.android.server.am.proto;
+package com.android.server.am;
 
 import "frameworks/base/core/proto/android/app/activitymanager.proto";
 import "frameworks/base/core/proto/android/app/enums.proto";
@@ -59,11 +59,11 @@
 message ActivityStackSupervisorProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
+  optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
   repeated ActivityDisplayProto displays = 2;
   optional KeyguardControllerProto keyguard_controller = 3;
   optional int32 focused_stack_id = 4;
-  optional .com.android.server.wm.proto.IdentifierProto resumed_activity = 5;
+  optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
   // Whether or not the home activity is the recents activity. This is needed for the CTS tests to
   // know what activity types to check for when invoking splitscreen multi-window.
   optional bool is_home_recents_component = 6;
@@ -73,7 +73,7 @@
 message ActivityDisplayProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
+  optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
   optional int32 id = 2;
   repeated ActivityStackProto stacks = 3;
 }
@@ -81,10 +81,10 @@
 message ActivityStackProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
+  optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
   optional int32 id = 2;
   repeated TaskRecordProto tasks = 3;
-  optional .com.android.server.wm.proto.IdentifierProto resumed_activity = 4;
+  optional .com.android.server.wm.IdentifierProto resumed_activity = 4;
   optional int32 display_id = 5;
   optional bool fullscreen = 6;
   optional .android.graphics.RectProto bounds = 7;
@@ -93,7 +93,7 @@
 message TaskRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
+  optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
   optional int32 id = 2;
   repeated ActivityRecordProto activities = 3;
   optional int32 stack_id = 4;
@@ -111,8 +111,8 @@
 message ActivityRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
-  optional .com.android.server.wm.proto.IdentifierProto identifier = 2;
+  optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
+  optional .com.android.server.wm.IdentifierProto identifier = 2;
   optional string state = 3;
   optional bool visible = 4;
   optional bool front_of_task = 5;
@@ -422,6 +422,8 @@
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
   message ServicesByUser {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int32 user_id = 1;
     repeated ServiceRecordProto service_records = 2;
   }
@@ -687,25 +689,14 @@
   }
   optional SleepStatus sleep_status = 27;
 
-  message VoiceProto {
+  message Voice {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional string session = 1;
     optional .android.os.PowerManagerProto.WakeLock wakelock = 2;
   }
-  optional VoiceProto running_voice = 28;
+  optional Voice running_voice = 28;
 
-  message VrControllerProto {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    enum VrMode {
-      FLAG_NON_VR_MODE = 0;
-      FLAG_VR_MODE = 1;
-      FLAG_PERSISTENT_VR_MODE = 2;
-    }
-    repeated VrMode vr_mode = 1;
-    optional int32 render_thread_id = 2;
-  }
   optional VrControllerProto vr_controller = 29;
 
   message DebugApp {
@@ -852,6 +843,19 @@
   optional string reason = 3;
 }
 
+// proto of class VrController.java
+message VrControllerProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  enum VrMode {
+    FLAG_NON_VR_MODE = 0;
+    FLAG_VR_MODE = 1;
+    FLAG_PERSISTENT_VR_MODE = 2;
+  }
+  repeated VrMode vr_mode = 1;
+  optional int32 render_thread_id = 2;
+}
+
 message ProcessOomProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
index c4ffe8c..9bfa794 100644
--- a/core/proto/android/server/animationadapter.proto
+++ b/core/proto/android/server/animationadapter.proto
@@ -20,7 +20,7 @@
 import "frameworks/base/core/proto/android/view/remote_animation_target.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
-package com.android.server.wm.proto;
+package com.android.server.wm;
 option java_multiple_files = true;
 
 message AnimationAdapterProto {
diff --git a/core/proto/android/server/appwindowthumbnail.proto b/core/proto/android/server/appwindowthumbnail.proto
index 8f48d75..54ad193 100644
--- a/core/proto/android/server/appwindowthumbnail.proto
+++ b/core/proto/android/server/appwindowthumbnail.proto
@@ -19,7 +19,7 @@
 import "frameworks/base/core/proto/android/server/surfaceanimator.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
-package com.android.server.wm.proto;
+package com.android.server.wm;
 option java_multiple_files = true;
 
 /**
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 9193129..122e5c4 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -20,7 +20,7 @@
 
 option java_multiple_files = true;
 
-import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/app/job/enums.proto";
 import "frameworks/base/core/proto/android/content/clipdata.proto";
 import "frameworks/base/core/proto/android/content/component_name.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
@@ -465,7 +465,7 @@
         message StopReasonCount {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-            optional .android.app.JobParametersProto.CancelReason reason = 1;
+            optional .android.app.job.StopReasonEnum reason = 1;
             optional int32 count = 2;
         }
         repeated StopReasonCount stop_reasons = 9;
@@ -516,7 +516,7 @@
         optional int32 job_id = 4;
         optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
         // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
-        optional .android.app.JobParametersProto.CancelReason stop_reason = 6;
+        optional .android.app.job.StopReasonEnum stop_reason = 6;
     }
     repeated HistoryEvent history_event = 1;
 }
diff --git a/core/proto/android/server/surfaceanimator.proto b/core/proto/android/server/surfaceanimator.proto
index dcc2b34..84560bc 100644
--- a/core/proto/android/server/surfaceanimator.proto
+++ b/core/proto/android/server/surfaceanimator.proto
@@ -20,7 +20,7 @@
 import "frameworks/base/core/proto/android/view/surfacecontrol.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
-package com.android.server.wm.proto;
+package com.android.server.wm;
 option java_multiple_files = true;
 
 /**
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 9598f24..063135d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -28,7 +28,7 @@
 import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
-package com.android.server.wm.proto;
+package com.android.server.wm;
 
 option java_multiple_files = true;
 
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
index 96a90bf..f502961 100644
--- a/core/proto/android/server/windowmanagertrace.proto
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -18,7 +18,7 @@
 
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 
-package com.android.server.wm.proto;
+package com.android.server.wm;
 
 option java_multiple_files = true;
 
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 5c40e5f..cccd2fe 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -93,7 +93,8 @@
     message ServiceProto {
         option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-        repeated string name = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+        // Package or component name.
+        repeated string name = 1;
         optional int32 user_id = 2;
         optional bool is_primary = 3;
     }
@@ -169,16 +170,16 @@
 message ZenRuleProto {
     option (android.msg_privacy).dest = DEST_EXPLICIT;
 
-    // Required for automatic (unique).
+    // Required for automatic ZenRules (unique).
     optional string id = 1;
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional string name = 2;
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional int64 creation_time_ms = 3 [
         (android.privacy).dest = DEST_AUTOMATIC
     ];
     optional bool enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Package name, only used for manual rules.
+    // Package name, only used for manual ZenRules.
     optional string enabler = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
     // User manually disabled this instance.
     optional bool is_snoozing = 6 [
@@ -188,7 +189,7 @@
         (android.privacy).dest = DEST_AUTOMATIC
     ];
 
-    // Required for automatic.
+    // Required for automatic ZenRules.
     optional string condition_id = 8;
     optional ConditionProto condition = 9;
     optional android.content.ComponentNameProto component = 10;
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index f8050a1..88bb4a6 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -110,6 +110,7 @@
         optional bool is_launched = 6;
         optional EnabledState enabled_state = 7;
         optional string last_disabled_app_caller = 8;
+        optional string suspending_package = 9;
     }
 
     // Name of package. e.g. "com.android.providers.telephony".
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index 60f8d8d..32975a5 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -28,6 +28,31 @@
     DATA_CONNECTION_POWER_STATE_UNKNOWN = 2147483647; // Java Integer.MAX_VALUE;
 }
 
+// Network type enums, primarily used by android/telephony/TelephonyManager.java.
+// Do not add negative types.
+enum NetworkTypeEnum {
+    NETWORK_TYPE_UNKNOWN = 0;
+    NETWORK_TYPE_GPRS = 1;
+    NETWORK_TYPE_EDGE = 2;
+    NETWORK_TYPE_UMTS = 3;
+    NETWORK_TYPE_CDMA = 4;
+    NETWORK_TYPE_EVDO_0 = 5;
+    NETWORK_TYPE_EVDO_A = 6;
+    NETWORK_TYPE_1XRTT = 7;
+    NETWORK_TYPE_HSDPA = 8;
+    NETWORK_TYPE_HSUPA = 9;
+    NETWORK_TYPE_HSPA = 10;
+    NETWORK_TYPE_IDEN = 11;
+    NETWORK_TYPE_EVDO_B = 12;
+    NETWORK_TYPE_LTE = 13;
+    NETWORK_TYPE_EHRPD = 14;
+    NETWORK_TYPE_HSPAP = 15;
+    NETWORK_TYPE_GSM = 16;
+    NETWORK_TYPE_TD_SCDMA = 17;
+    NETWORK_TYPE_IWLAN = 18;
+    NETWORK_TYPE_LTE_CA = 19;
+}
+
 // Signal strength levels, primarily used by android/telephony/SignalStrength.java.
 enum SignalStrengthEnum {
     SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b11a33..a5926ab 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -180,6 +180,8 @@
     <protected-broadcast
         android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
@@ -1341,6 +1343,13 @@
         android:label="@string/permlab_changeWifiState"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
+         <p>Only granted to applications that are currently bound by the
+         system for creating and managing IPsec-based interfaces.
+    -->
+    <permission android:name="android.permission.MANAGE_IPSEC_TUNNELS"
+        android:protectionLevel="signature|appop" />
+
     <!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.READ_WIFI_CREDENTIAL"
@@ -1441,6 +1450,13 @@
         android:label="@string/permlab_bluetooth"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi Allows an application to suspend other apps, which will prevent the user
+         from using them until they are unsuspended.
+         @hide
+    -->
+    <permission android:name="android.permission.SUSPEND_APPS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows applications to discover and pair bluetooth devices.
          <p>Protection level: normal
     -->
@@ -3310,8 +3326,9 @@
     <permission android:name="android.permission.BACKUP"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Allows application to manage RecoverableKeyStoreLoader.
-    <p>Not for use by third-party applications.
+    <!-- @SystemApi Allows application to manage
+         {@link android.security.keystore.recovery.RecoveryController}.
+         <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.RECOVER_KEYSTORE"
         android:protectionLevel="signature|privileged" />
@@ -3754,10 +3771,17 @@
 
     <!-- Must be required by system/priv apps when accessing the sound trigger
          APIs given by {@link SoundTriggerManager}.
-         @hide <p>Not for use by third-party applications.</p> -->
+         @hide
+         @SystemApi -->
     <permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by system/priv apps implementing sound trigger detection services
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
          Provisioning app. If requesting app does not have permission, it will be ignored.
          @hide -->
@@ -3879,6 +3903,12 @@
     <permission android:name="android.permission.WATCH_APPOPS"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to directly open the "Open by default" page inside a package's
+         Details screen.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.OPEN_APPLICATION_DETAILS_OPEN_BY_DEFAULT_PAGE"
+                android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 5c5b985..fa69038 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -35,8 +35,8 @@
         <LinearLayout
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
-            android:paddingLeft="16dp"
-            android:paddingRight="16dp"
+            android:paddingStart="16dp"
+            android:paddingEnd="16dp"
             android:orientation="vertical">
 
             <LinearLayout
@@ -52,7 +52,7 @@
 
                 <TextView
                     android:id="@+id/autofill_save_title"
-                    android:paddingLeft="8dp"
+                    android:paddingStart="8dp"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
                     android:text="@string/autofill_save_title"
diff --git a/core/res/res/layout/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml
index 398bfb1..2d214b3 100644
--- a/core/res/res/layout/shutdown_dialog.xml
+++ b/core/res/res/layout/shutdown_dialog.xml
@@ -29,7 +29,7 @@
     <TextView
         android:id="@id/text1"
         android:layout_width="wrap_content"
-        android:layout_height="32dp"
+        android:layout_height="32sp"
         android:text="@string/shutdown_progress"
         android:textDirection="locale"
         android:textSize="24sp"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4a72bf9..3135455 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2151,6 +2151,9 @@
             @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
             -->
             <enum name="shortEdges" value="1" />
+            <!-- Use {@code shortEdges} instead. This is temporarily here to unblock pushing the SDK
+                 until all usages have been migrated to {@code shortEdges} -->
+            <enum name="always" value="1" />
             <!-- The window is never allowed to overlap with the DisplayCutout area.
             <p>
             This should be used with windows that transiently set {@code SYSTEM_UI_FLAG_FULLSCREEN}
@@ -5965,6 +5968,9 @@
              in the encoded data. Setting this to infinite (-1) will result in the
              animation repeating as long as it is displayed (once start() is called). -->
         <attr name="repeatCount"/>
+        <!-- When true, automatically start animating. The default is false, meaning
+             that the animation will not start until start() is called. -->
+        <attr name="autoStart" />
     </declare-styleable>
 
     <!-- Drawable used to draw bitmaps. -->
@@ -7981,8 +7987,6 @@
              android.content.pm.PackageInfo#getLongVersionCode()} for the target package.
         -->
         <attr name="maxLongVersionCode" format="string" />
-        <!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
-        <attr name="urlBarResourceId" format="string" />
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 722102e..449d3e7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,13 +130,14 @@
     <drawable name="notification_template_divider_media">#29ffffff</drawable>
     <color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color>
     <color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color>
-    <color name="notification_secondary_text_color_light">@color/secondary_text_material_light</color>
-    <color name="notification_secondary_text_color_dark">@color/secondary_text_material_dark</color>
+    <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color>
+    <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color>
+    <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
+    <color name="notification_default_color_light">#a3202124</color>
 
     <color name="notification_material_background_color">#ffffffff</color>
 
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
-    <color name="notification_icon_default_color">@color/notification_default_color</color>
 
     <color name="notification_progress_background_color">@color/secondary_text_material_light</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cf13d1c..55c17b9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1277,7 +1277,7 @@
          in darkness (although they may not be visible in a bright room). -->
     <integer name="config_screenBrightnessDark">1</integer>
 
-    <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
+    <!-- Array of light sensor lux values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 control points as follows:
          (1-based arrays)
 
@@ -1289,18 +1289,18 @@
 
          The control points must be strictly increasing.  Each control point
          corresponds to an entry in the brightness backlight values arrays.
-         For example, if LUX == level[1] (first element of the levels array)
+         For example, if lux == level[1] (first element of the levels array)
          then the brightness will be determined by value[2] (second element
          of the brightness values array).
 
          Spline interpolation is used to determine the auto-brightness
-         backlight values for LUX levels between these control points.
+         backlight values for lux levels between these control points.
 
          Must be overridden in platform specific overlays -->
     <integer-array name="config_autoBrightnessLevels">
     </integer-array>
 
-    <!-- Array of output values for LCD backlight corresponding to the LUX values
+    <!-- Array of output values for LCD backlight corresponding to the lux values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
          The brightness values must be between 0 and 255 and be non-decreasing.
@@ -1324,7 +1324,7 @@
     <array name="config_autoBrightnessDisplayValuesNits">
     </array>
 
-    <!-- Array of output values for button backlight corresponding to the LUX values
+    <!-- Array of output values for button backlight corresponding to the luX values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
          The brightness values must be between 0 and 255 and be non-decreasing.
@@ -1332,7 +1332,7 @@
     <integer-array name="config_autoBrightnessButtonBacklightValues">
     </integer-array>
 
-    <!-- Array of output values for keyboard backlight corresponding to the LUX values
+    <!-- Array of output values for keyboard backlight corresponding to the lux values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
          The brightness values must be between 0 and 255 and be non-decreasing.
@@ -1996,7 +1996,7 @@
 
     <!-- If true, the doze component is not started until after the screen has been
          turned off and the screen off animation has been performed. -->
-    <bool name="config_dozeAfterScreenOff">false</bool>
+    <bool name="config_dozeAfterScreenOffByDefault">false</bool>
 
     <!-- Doze: should the TYPE_PICK_UP_GESTURE sensor be used as a pulse signal. -->
     <bool name="config_dozePulsePickup">false</bool>
@@ -2911,6 +2911,10 @@
          is non-interactive. -->
     <bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
 
+    <!-- Allow the gesture power + volume up to change the ringer mode while the device
+         is interactive. -->
+    <bool name="config_volumeHushGestureEnabled">true</bool>
+
     <!-- Name of the component to handle network policy notifications. If present,
          disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
     <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
@@ -2956,8 +2960,8 @@
     <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item>
 
     <!-- The default gravity for the picture-in-picture window.
-         Currently, this maps to Gravity.TOP | Gravity.RIGHT -->
-    <integer name="config_defaultPictureInPictureGravity">0x35</integer>
+         Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
+    <integer name="config_defaultPictureInPictureGravity">0x55</integer>
 
     <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture.  Any
          ratio smaller than this is considered too tall and thin to be usable. Currently, this
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2c0deed..0246c80 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2869,8 +2869,6 @@
       <public name="outlineSpotShadowColor" />
       <public name="outlineAmbientShadowColor" />
       <public name="maxLongVersionCode" />
-      <!-- TODO(b/74445943): STOPSHIP (urlBarResourceId should be removed after P DP2 is branched)-->
-      <public name="urlBarResourceId" />
       <!-- @hide @SystemApi -->
       <public name="userRestriction" />
       <public name="textFontWeight" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index daad866..0ef93e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -159,6 +159,9 @@
     <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
     <string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
 
+    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. -->
+    <string name="RestrictedStateContentMsimTemplate">Temporarily turned off by your carrier for SIM <xliff:g id="simNumber" example="1">%d</xliff:g></string>
+
     <!-- Displayed to tell the user that they should switch their network preference. -->
     <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach mobile network</string>
     <!-- Displayed to tell the user that they should switch their network preference. -->
@@ -227,14 +230,14 @@
     <string name="roamingTextSearching">Searching for Service</string>
 
     <!-- Displayed when WFC registration fails -->
-    <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
+    <string name="wfcRegErrorTitle">Couldn\u2019t set up Wi\u2011Fi calling</string>
     <!-- WFC Operator Error Messages showed as alerts -->
     <string-array name="wfcOperatorErrorAlertMessages">
         <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
     </string-array>
     <!-- WFC Operator Error Messages showed as notifications -->
     <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
+        <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
     </string-array>
     <!-- Template for showing mobile network operator name while WFC is active -->
     <string-array name="wfcSpnFormats">
@@ -319,9 +322,9 @@
     <!-- A notification is shown when there is a sync error.  This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
     <string name="contentServiceSync">Sync</string>
     <!-- A notification is shown when there is a sync error.  This is the title of the notification.  It will be seen in the pull-down notification tray. -->
-    <string name="contentServiceSyncNotificationTitle">Sync</string>
+    <string name="contentServiceSyncNotificationTitle">Can\'t sync</string>
     <!-- A notification is shown when there is a sync error.  This is the message of the notification.  It describes the error, in this case is there were too many deletes. The argument is the type of content, for example Gmail or Calendar. It will be seen in the pull-down notification tray. -->
-    <string name="contentServiceTooManyDeletesNotificationDesc">Too many <xliff:g id="content_type">%s</xliff:g> deletes.</string>
+    <string name="contentServiceTooManyDeletesNotificationDesc">Attempted to delete too many <xliff:g id="content_type">%s</xliff:g>.</string>
 
     <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
     <string name="low_memory" product="tablet">Tablet storage is full. Delete some files to free space.</string>
@@ -670,7 +673,7 @@
     <string name="permgroupdesc_contacts">access your contacts</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_contacts">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your contacts</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your contacts?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_location">Location</string>
@@ -678,7 +681,7 @@
     <string name="permgroupdesc_location">access this device\'s location</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_location">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\'s location</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\'s location?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_calendar">Calendar</string>
@@ -686,7 +689,7 @@
     <string name="permgroupdesc_calendar">access your calendar</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_calendar">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your calendar</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your calendar?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_sms">SMS</string>
@@ -694,7 +697,7 @@
     <string name="permgroupdesc_sms">send and view SMS messages</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_sms">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send and view SMS messages</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send and view SMS messages?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_storage">Storage</string>
@@ -702,7 +705,7 @@
     <string name="permgroupdesc_storage">access photos, media, and files on your device</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_storage">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos, media, and files on your device</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos, media, and files on your device?</string>
 
     <!-- Title of a category of application permissioncds, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_microphone">Microphone</string>
@@ -710,7 +713,7 @@
     <string name="permgroupdesc_microphone">record audio</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_microphone">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_camera">Camera</string>
@@ -718,7 +721,7 @@
     <string name="permgroupdesc_camera">take pictures and record video</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_camera">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_phone">Phone</string>
@@ -726,7 +729,7 @@
     <string name="permgroupdesc_phone">make and manage phone calls</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_phone">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to make and manage phone calls</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to make and manage phone calls?</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_sensors">Body Sensors</string>
@@ -734,7 +737,7 @@
     <string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
     <!-- Message shown to the user when the apps requests permission from this group -->
     <string name="permgrouprequest_sensors">Allow
-        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access sensor data about your vital signs</string>
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access sensor data about your vital signs?</string>
 
     <!-- Title for the capability of an accessibility service to retrieve window content. -->
     <string name="capability_title_canRetrieveWindowContent">Retrieve window content</string>
@@ -3007,8 +3010,7 @@
         limit</string>
 
     <!-- Notification details to tell the user that a process has exceeded its memory limit. -->
-    <string name="dump_heap_notification_detail">Heap dump has been collected;
-        tap to share</string>
+    <string name="dump_heap_notification_detail">Heap dump collected. Tap to share.</string>
 
     <!-- Title of dialog prompting the user to share a heap dump. -->
     <string name="dump_heap_title">Share heap dump?</string>
@@ -3086,13 +3088,13 @@
     <string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
     <!-- Notification title for a nearby carrier wireless network.-->
     <string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
-    <!-- Notification title when the system is connecting to the specified open network. The network name is specified in the notification content. -->
-    <string name="wifi_available_title_connecting">Connecting to open Wi\u2011Fi network</string>
-    <!-- Notification title when the system has connected to the open network. The network name is specified in the notification content. -->
+    <!-- Notification title when the system is connecting to the specified network. The network name is specified in the notification content. -->
+    <string name="wifi_available_title_connecting">Connecting to Wi\u2011Fi network</string>
+    <!-- Notification title when the system has connected to the network. The network name is specified in the notification content. -->
     <string name="wifi_available_title_connected">Connected to Wi\u2011Fi network</string>
-    <!-- Notification title when the system failed to connect to the specified open network. -->
+    <!-- Notification title when the system failed to connect to the specified network. -->
     <string name="wifi_available_title_failed_to_connect">Could not connect to Wi\u2011Fi network</string>
-    <!-- Notification content when the system failed to connect to the specified open network. This informs the user that tapping on this notification will open the wifi picker. -->
+    <!-- Notification content when the system failed to connect to the specified network. This informs the user that tapping on this notification will open the wifi picker. -->
     <string name="wifi_available_content_failed_to_connect">Tap to see all networks</string>
     <!-- Notification action name for connecting to the network specified in the notification body. -->
     <string name="wifi_available_action_connect">Connect</string>
@@ -3574,7 +3576,7 @@
     <string name="vpn_lockdown_disconnected">Disconnected from always-on VPN</string>
     <!-- Notification title when error connecting to always-on VPN, a VPN that's set to always stay
          connected. -->
-    <string name="vpn_lockdown_error">Always-on VPN error</string>
+    <string name="vpn_lockdown_error">Couldn\'t connect to always-on VPN</string>
     <!-- Notification body that indicates user can touch to configure always-on VPN, a VPN that's
          set to always stay connected. -->
     <string name="vpn_lockdown_config">Change network or VPN settings</string>
@@ -3591,8 +3593,8 @@
 
     <!-- Strings for car mode notification -->
     <!-- Shown when car mode is enabled -->
-    <string name="car_mode_disable_notification_title">Car mode enabled</string>
-    <string name="car_mode_disable_notification_message">Tap to exit car mode.</string>
+    <string name="car_mode_disable_notification_title">Driving app is running</string>
+    <string name="car_mode_disable_notification_message">Tap to exit driving app.</string>
 
     <!-- Strings for tethered notification -->
     <!-- Shown when the device is tethered -->
@@ -4476,7 +4478,7 @@
     <string name="package_deleted_device_owner">Deleted by your admin</string>
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
-    <string name="battery_saver_description">To help improve battery life, Battery Saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery Saver turns off automatically when your device is charging.</string>
+    <string name="battery_saver_description">To extend battery life, Battery Saver reduces your device\'s performance and limits or turns off vibration, location services, and background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery Saver turns off automatically when your device is charging.</string>
 
     <!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
     <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
@@ -4579,14 +4581,14 @@
 
     <!-- Displayed when the USSD/SS request is modified by STK CC to a
     different request. This will be displayed in a toast. -->
-    <string name="stk_cc_ussd_to_dial">USSD request is modified to DIAL request.</string>
-    <string name="stk_cc_ussd_to_ss">USSD request is modified to SS request.</string>
-    <string name="stk_cc_ussd_to_ussd">USSD request is modified to new USSD request.</string>
-    <string name="stk_cc_ussd_to_dial_video">USSD request is modified to Video DIAL request.</string>
-    <string name="stk_cc_ss_to_dial">SS request is modified to DIAL request.</string>
-    <string name="stk_cc_ss_to_dial_video">SS request is modified to Video DIAL request.</string>
-    <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
-    <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
+    <string name="stk_cc_ussd_to_dial">USSD request changed to regular call</string>
+    <string name="stk_cc_ussd_to_ss">USSD request changed to SS request</string>
+    <string name="stk_cc_ussd_to_ussd">Changed to new USSD request</string>
+    <string name="stk_cc_ussd_to_dial_video">USSD request changed to video call</string>
+    <string name="stk_cc_ss_to_dial">SS request changed to regular call</string>
+    <string name="stk_cc_ss_to_dial_video">SS request changed to video call</string>
+    <string name="stk_cc_ss_to_ussd">SS request changed to USSD request</string>
+    <string name="stk_cc_ss_to_ss">Changed to new SS request</string>
 
     <!-- Content description of the work profile icon in the notification. -->
     <string name="notification_work_profile_content_description">Work profile</string>
@@ -4834,6 +4836,12 @@
     <string name="mmcc_illegal_ms">SIM not allowed for voice</string>
     <string name="mmcc_illegal_me">Phone not allowed for voice</string>
 
+    <!-- Title of notification when UE fails to register network with MM reject cause code when multiple SIMs are active. -->
+    <string name="mmcc_authentication_reject_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+    <string name="mmcc_imsi_unknown_in_hlr_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not provisioned</string>
+    <string name="mmcc_illegal_ms_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+    <string name="mmcc_illegal_me_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+
     <!-- Popup window default title to be read by a screen reader-->
     <string name="popup_window_default_title">Popup Window</string>
 
@@ -4869,12 +4877,6 @@
     -->
     <string name="shortcut_disabled_reason_unknown">Shortcut is disabled</string>
 
-    <!--Battery saver warning. STOPSHIP: Remove it eventually. -->
-    <string name="battery_saver_warning" translatable="false">\"Extreme\" battery saver activated.\n\nSee the details at: go/extreme-battery-saver\n\nEBS aggressively throttles background apps and changes screen-off behavior.\n</string>
-
-    <!--Battery saver warning. STOPSHIP: Remove it eventually. -->
-    <string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string>
-
     <!-- Label for the uninstall button on the harmful app warning dialog. [CHAR LIMIT=20] -->
     <string name="harmful_app_warning_uninstall">UNINSTALL</string>
     <!-- Label for the open anyway button on the harmful app warning dialog. [CHAR LIMIT=20] -->
@@ -4889,6 +4891,9 @@
     <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
     <string name="screenshot_edit">Edit</string>
 
+    <string name="volume_dialog_ringer_guidance_vibrate">Calls and notifications will vibrate</string>
+    <string name="volume_dialog_ringer_guidance_silent">Calls and notifications will be muted</string>
+
     <!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
     <string name="notification_channel_system_changes">System changes</string>
     <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
@@ -4901,4 +4906,9 @@
     <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
     <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
     <string name="zen_upgrade_notification_content">Tap to check what\'s blocked.</string>
+
+    <!-- Application name displayed in notifications [CHAR LIMIT=60] -->
+    <string name="notification_app_name_system">System</string>
+    <!-- Application name displayed in notifications [CHAR LIMIT=60] -->
+    <string name="notification_app_name_settings">Settings</string>
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index fa3cf2f..b8a046f 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -473,7 +473,7 @@
     <style name="TextAppearance.Material.Notification.Reply" />
 
     <style name="TextAppearance.Material.Notification.Title">
-        <item name="textColor">@color/notification_primary_text_color_light</item>
+        <item name="fontFamily">sans-serif-medium</item>
         <item name="textSize">@dimen/notification_title_text_size</item>
     </style>
 
@@ -482,7 +482,6 @@
     </style>
 
     <style name="TextAppearance.Material.Notification.Info">
-        <item name="textColor">@color/notification_secondary_text_color_light</item>
         <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 55e9157..b9215f8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -579,6 +579,7 @@
   <java-symbol type="string" name="RestrictedOnEmergencyTitle" />
   <java-symbol type="string" name="RestrictedOnNormalTitle" />
   <java-symbol type="string" name="RestrictedStateContent" />
+  <java-symbol type="string" name="RestrictedStateContentMsimTemplate" />
   <java-symbol type="string" name="notification_channel_network_alert" />
   <java-symbol type="string" name="notification_channel_call_forward" />
   <java-symbol type="string" name="notification_channel_emergency_callback" />
@@ -1024,6 +1025,8 @@
   <java-symbol type="string" name="volume_icon_description_media" />
   <java-symbol type="string" name="volume_icon_description_notification" />
   <java-symbol type="string" name="volume_icon_description_ringer" />
+  <java-symbol type="string" name="volume_dialog_ringer_guidance_vibrate" />
+  <java-symbol type="string" name="volume_dialog_ringer_guidance_silent" />
   <java-symbol type="string" name="wait" />
   <java-symbol type="string" name="webpage_unresponsive" />
   <java-symbol type="string" name="whichApplication" />
@@ -1817,7 +1820,7 @@
   <java-symbol type="bool" name="config_notificationHeaderClickableForExpand" />
   <java-symbol type="bool" name="config_enableNightMode" />
   <java-symbol type="bool" name="config_tintNotificationActionButtons" />
-  <java-symbol type="bool" name="config_dozeAfterScreenOff" />
+  <java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
   <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
   <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
   <java-symbol type="bool" name="config_enableHardwareFlpOverlay" />
@@ -2029,6 +2032,10 @@
   <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr" />
   <java-symbol type="string" name="mmcc_illegal_ms" />
   <java-symbol type="string" name="mmcc_illegal_me" />
+  <java-symbol type="string" name="mmcc_authentication_reject_msim_template" />
+  <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr_msim_template" />
+  <java-symbol type="string" name="mmcc_illegal_ms_msim_template" />
+  <java-symbol type="string" name="mmcc_illegal_me_msim_template" />
   <java-symbol type="string" name="notification_listener_binding_label" />
   <java-symbol type="string" name="vr_listener_binding_label" />
   <java-symbol type="string" name="condition_provider_service_binding_label" />
@@ -2115,7 +2122,6 @@
   <java-symbol type="layout" name="notification_template_material_big_text" />
   <java-symbol type="layout" name="notification_template_header" />
   <java-symbol type="layout" name="notification_material_media_action" />
-  <java-symbol type="color" name="notification_icon_default_color" />
   <java-symbol type="color" name="notification_progress_background_color" />
   <java-symbol type="id" name="media_actions" />
 
@@ -2621,6 +2627,7 @@
   <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
   <java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
   <java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
+  <java-symbol type="bool" name="config_volumeHushGestureEnabled" />
 
   <java-symbol type="drawable" name="platlogo_m" />
 
@@ -2779,7 +2786,6 @@
   <java-symbol type="drawable" name="ic_user_secure" />
 
   <java-symbol type="string" name="android_upgrading_notification_title" />
-  <java-symbol type="string" name="android_upgrading_notification_body" />
 
   <java-symbol type="string" name="usb_mtp_launch_notification_title" />
   <java-symbol type="string" name="usb_mtp_launch_notification_description" />
@@ -2965,6 +2971,8 @@
   <java-symbol type="color" name="notification_primary_text_color_dark" />
   <java-symbol type="color" name="notification_secondary_text_color_light" />
   <java-symbol type="color" name="notification_secondary_text_color_dark" />
+  <java-symbol type="color" name="notification_default_color_light" />
+  <java-symbol type="color" name="notification_default_color_dark" />
 
   <java-symbol type="string" name="app_category_game" />
   <java-symbol type="string" name="app_category_audio" />
@@ -3232,6 +3240,7 @@
   <java-symbol type="id" name="remote_input_progress" />
   <java-symbol type="id" name="remote_input_send" />
   <java-symbol type="id" name="remote_input" />
+  <java-symbol type="dimen" name="notification_content_margin" />
   <java-symbol type="dimen" name="slice_shortcut_size" />
   <java-symbol type="dimen" name="slice_icon_size" />
   <java-symbol type="dimen" name="slice_padding" />
@@ -3241,7 +3250,6 @@
   <java-symbol type="string" name="shortcut_restore_not_supported" />
   <java-symbol type="string" name="shortcut_restore_signature_mismatch" />
   <java-symbol type="string" name="shortcut_restore_unknown_issue" />
-  <java-symbol type="string" name="battery_saver_warning" />
 
   <!-- From media projection -->
   <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
@@ -3252,7 +3260,6 @@
   <java-symbol type="string" name="unsupported_compile_sdk_message" />
   <java-symbol type="string" name="unsupported_compile_sdk_check_update" />
 
-  <java-symbol type="string" name="battery_saver_warning_title" />
   <java-symbol type="string" name="keyguard_accessibility_pattern_unlock" />
   <java-symbol type="string" name="keyguard_accessibility_pin_unlock" />
   <java-symbol type="string" name="keyguard_accessibility_sim_pin_unlock" />
@@ -3301,4 +3308,7 @@
 
   <java-symbol type="string" name="config_managed_provisioning_package" />
 
+  <java-symbol type="string" name="notification_app_name_system" />
+  <java-symbol type="string" name="notification_app_name_settings" />
+
 </resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 2d3cd1c..f1b61a7 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="5">
+<zen version="6">
     <allow alarms="true" media="true" system="false" calls="false" messages="false" reminders="false"
            events="false" />
     <!-- all visual effects that exist as of P -->
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4628aa9..a99e139 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -22,6 +22,7 @@
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
 import android.content.Intent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -34,6 +35,8 @@
 
 /**
  * Test for verifying {@link android.app.ActivityThread} class.
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -63,15 +66,23 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
+    @Test
+    public void testSleepAndStop() throws Exception {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+
+        appThread.scheduleSleeping(activity.getActivityToken(), true /* sleeping */);
+        appThread.scheduleTransaction(newStopTransaction(activity));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
     private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
         final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
-                null, 0, new MergedConfiguration(),
-                false /* preserveWindow */);
+                null, 0, new MergedConfiguration(), false /* preserveWindow */);
         final ResumeActivityItem resumeStateRequest =
                 ResumeActivityItem.obtain(true /* isForward */);
-        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
-        final ClientTransaction transaction =
-                ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+        final ClientTransaction transaction = newTransaction(activity);
         transaction.addCallback(callbackItem);
         transaction.setLifecycleStateRequest(resumeStateRequest);
 
@@ -81,14 +92,28 @@
     private static ClientTransaction newResumeTransaction(Activity activity) {
         final ResumeActivityItem resumeStateRequest =
                 ResumeActivityItem.obtain(true /* isForward */);
-        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
-        final ClientTransaction transaction =
-                ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+        final ClientTransaction transaction = newTransaction(activity);
         transaction.setLifecycleStateRequest(resumeStateRequest);
 
         return transaction;
     }
 
+    private static ClientTransaction newStopTransaction(Activity activity) {
+        final StopActivityItem stopStateRequest =
+                StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */);
+
+        final ClientTransaction transaction = newTransaction(activity);
+        transaction.setLifecycleStateRequest(stopStateRequest);
+
+        return transaction;
+    }
+
+    private static ClientTransaction newTransaction(Activity activity) {
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+        return ClientTransaction.obtain(appThread, activity.getActivityToken());
+    }
+
     // Test activity
     public static class TestActivity extends Activity {
     }
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index 8b8cf67..63d4080 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -16,7 +16,8 @@
 
 package android.net;
 
-import android.net.SntpClient;
+import android.content.Context;
+import android.test.AndroidTestCase;
 import android.util.Log;
 import libcore.util.HexEncoding;
 
@@ -26,10 +27,9 @@
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.util.Arrays;
-import junit.framework.TestCase;
 
 
-public class SntpClientTest extends TestCase {
+public class SntpClientTest extends AndroidTestCase {
     private static final String TAG = "SntpClientTest";
 
     private static final int ORIGINATE_TIME_OFFSET = 24;
@@ -61,20 +61,29 @@
     private final SntpTestServer mServer = new SntpTestServer();
     private final SntpClient mClient = new SntpClient();
 
+    private Network mNetwork;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        ConnectivityManager mCM = getContext().getSystemService(ConnectivityManager.class);
+        mNetwork = mCM.getActiveNetwork();
+    }
+
     public void testBasicWorkingSntpClientQuery() throws Exception {
         mServer.setServerReply(HexEncoding.decode(WORKING_VERSION4.toCharArray(), false));
-        assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+        assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
         assertEquals(1, mServer.numRepliesSent());
     }
 
     public void testDnsResolutionFailure() throws Exception {
-        assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", 5000));
+        assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", 5000, mNetwork));
     }
 
     public void testTimeoutFailure() throws Exception {
         mServer.clearServerReply();
-        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
         assertEquals(0, mServer.numRepliesSent());
     }
@@ -83,7 +92,7 @@
         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
         reply[0] |= (byte) 0xc0;
         mServer.setServerReply(reply);
-        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
         assertEquals(1, mServer.numRepliesSent());
     }
@@ -95,7 +104,8 @@
             reply[0] &= (byte) 0xf8;
             reply[0] |= (byte) i;
             mServer.setServerReply(reply);
-            final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500);
+            final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500,
+                    mNetwork);
             switch (i) {
                 case NTP_MODE_SERVER:
                 case NTP_MODE_BROADCAST:
@@ -119,7 +129,8 @@
             final String logMsg = "stratum: " + i;
             reply[1] = (byte) i;
             mServer.setServerReply(reply);
-            final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500);
+            final boolean rval = mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500,
+                    mNetwork);
             if (STRATUM_MIN <= i && i <= STRATUM_MAX) {
                 assertTrue(logMsg, rval);
             } else {
@@ -134,7 +145,7 @@
         final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
         Arrays.fill(reply, TRANSMIT_TIME_OFFSET, TRANSMIT_TIME_OFFSET + 8, (byte) 0x00);
         mServer.setServerReply(reply);
-        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
+        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
         assertEquals(1, mServer.numRepliesSent());
     }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 04cdb0b..a504ab9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -117,7 +117,7 @@
                     Settings.Global.APP_STANDBY_ENABLED,
                     Settings.Global.ASSISTED_GPS_ENABLED,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
-                    Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES,
+                    Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -129,6 +129,7 @@
                     Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
+                    Settings.Global.BLE_SCAN_BACKGROUND_MODE,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
@@ -169,6 +170,7 @@
                     Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
                     Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
                     Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
+                    Settings.Global.CPU_SCALING_ENABLED,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
                     Settings.Global.DATABASE_CREATION_BUILDID,
@@ -266,6 +268,7 @@
                     Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
                     Settings.Global.LTE_SERVICE_FORCED,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
+                    Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
@@ -369,10 +372,12 @@
                     Settings.Global.SMS_SHORT_CODE_RULE,
                     Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL,
                     Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL,
+                    Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
                     Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
                     Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                     Settings.Global.STORAGE_BENCHMARK_INTERVAL,
                     Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
+                    Settings.Global.SWAP_ENABLED,
                     Settings.Global.SYNC_MANAGER_CONSTANTS,
                     Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                     Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
@@ -445,9 +450,8 @@
                     Settings.Global.WIFI_REENABLE_DELAY_MS,
                     Settings.Global.WIFI_SAVED_STATE,
                     Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
-                    Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
-                    Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
                     Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
+                    Settings.Global.WIFI_SCORE_PARAMS,
                     Settings.Global.WIFI_SLEEP_POLICY,
                     Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
                     Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED,
@@ -570,6 +574,7 @@
                  Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
                  Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
                  Settings.Secure.USER_SETUP_COMPLETE,
+                 Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
                  Settings.Secure.VOICE_INTERACTION_SERVICE,
                  Settings.Secure.VOICE_RECOGNITION_SERVICE,
                  Settings.Secure.INSTANT_APPS_ENABLED,
@@ -578,7 +583,9 @@
                  Settings.Secure.KEYGUARD_SLICE_URI,
                  Settings.Secure.PARENTAL_CONTROL_ENABLED,
                  Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
-                 Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING);
+                 Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
+                 Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
+                 Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
new file mode 100644
index 0000000..a5a3ca9
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.security.keystore.recovery;
+
+import static android.security.keystore.recovery.TrustedRootCertificates.getRootCertificates;
+
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TrustedRootCertificatesTest {
+    private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS =
+            "GoogleCloudKeyVaultServiceV1";
+
+    @Test
+    public void getRootCertificates_listsGoogleCloudVaultV1Certificate() {
+        Map<String, X509Certificate> certificates = getRootCertificates();
+
+        assertTrue(certificates.containsKey(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS));
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
similarity index 70%
rename from services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
rename to core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index 7195d62..7f0eb43 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,26 @@
  * limitations under the License.
  */
 
-package com.android.server.locksettings.recoverablekeystore.certificate;
+package android.security.keystore.recovery;
 
-import java.security.cert.X509Certificate;
+import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert;
 
-/**
- * Holds the X509 certificate of the trusted root CA cert for the recoverable key store service.
- *
- * TODO: Read the certificate from a PEM file directly and remove this class.
- */
-public final class TrustedRootCert {
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
-    private  static final String TRUSTED_ROOT_CERT_BASE64 = ""
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.cert.CertificateException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class X509CertificateParsingUtilsTest {
+
+    private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64 = ""
             + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
             + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
             + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
@@ -55,20 +63,20 @@
             + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
             + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
 
-    /**
-     * The X509 certificate of the trusted root CA cert for the recoverable key store service.
-     *
-     * TODO: Change it to the production certificate root CA before the final launch.
-     */
-    public static final X509Certificate TRUSTED_ROOT_CERT;
+    private static final String INVALID_BASE64 = "EVTVA==";
 
-    static {
+    @Test
+    public void decodeBase64Cert_decodesValidCertificate() throws Exception {
+        assertNotNull(decodeBase64Cert(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64));
+    }
+
+    @Test
+    public void decodeBase64Cert_throwsCertificateExceptionForInvalidBase64() {
         try {
-            TRUSTED_ROOT_CERT = CertUtils.decodeCert(
-                    CertUtils.decodeBase64(TRUSTED_ROOT_CERT_BASE64));
-        } catch (CertParsingException e) {
-            // Should not happen
-            throw new RuntimeException(e);
+            decodeBase64Cert(INVALID_BASE64);
+            fail("Did not throw when attempting to decode invalid base-64");
+        } catch (CertificateException e) {
+            // expected
         }
     }
 }
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
new file mode 100644
index 0000000..c205f96
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.style.QuoteSpan;
+import android.text.style.UnderlineSpan;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannableStringNoCopyTest {
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(first);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(new CustomSpannable(first));
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(
+                new CustomSpannable(first), false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    // A custom implementation of Spannable.
+    private static class CustomSpannable implements Spannable {
+        private final @NonNull Spannable mText;
+
+        CustomSpannable(@NonNull Spannable text) {
+            mText = text;
+        }
+
+        @Override
+        public void setSpan(Object what, int start, int end, int flags) {
+            mText.setSpan(what, start, end, flags);
+        }
+
+        @Override
+        public void removeSpan(Object what) {
+            mText.removeSpan(what);
+        }
+
+        @Override
+        public <T> T[] getSpans(int start, int end, Class<T> type) {
+            return mText.getSpans(start, end, type);
+        }
+
+        @Override
+        public int getSpanStart(Object tag) {
+            return mText.getSpanStart(tag);
+        }
+
+        @Override
+        public int getSpanEnd(Object tag) {
+            return mText.getSpanEnd(tag);
+        }
+
+        @Override
+        public int getSpanFlags(Object tag) {
+            return mText.getSpanFlags(tag);
+        }
+
+        @Override
+        public int nextSpanTransition(int start, int limit, Class type) {
+            return mText.nextSpanTransition(start, limit, type);
+        }
+
+        @Override
+        public int length() {
+            return mText.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return mText.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return mText.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return mText.toString();
+        }
+    };
+}
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
new file mode 100644
index 0000000..0680924
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.style.QuoteSpan;
+import android.text.style.UnderlineSpan;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannedStringNoCopyTest {
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(first);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    @Test
+    public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // By default, copy NoCopySpans
+        final SpannedString copied = new SpannedString(new CustomSpanned(first));
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(3, spans.length);
+    }
+
+    @Test
+    public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() {
+        final SpannableString first = new SpannableString("t\nest data");
+        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
+        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
+
+        // Do not copy NoCopySpan if specified so.
+        final SpannedString copied = new SpannedString(
+                new CustomSpanned(first), false /* copyNoCopySpan */);
+        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
+        assertNotNull(spans);
+        assertEquals(2, spans.length);
+
+        for (int i = 0; i < spans.length; i++) {
+            assertFalse(spans[i] instanceof NoCopySpan);
+        }
+    }
+
+    // A custom implementation of Spanned
+    private static class CustomSpanned implements Spanned {
+        private final @NonNull Spanned mText;
+
+        CustomSpanned(@NonNull Spannable text) {
+            mText = text;
+        }
+
+        @Override
+        public <T> T[] getSpans(int start, int end, Class<T> type) {
+            return mText.getSpans(start, end, type);
+        }
+
+        @Override
+        public int getSpanStart(Object tag) {
+            return mText.getSpanStart(tag);
+        }
+
+        @Override
+        public int getSpanEnd(Object tag) {
+            return mText.getSpanEnd(tag);
+        }
+
+        @Override
+        public int getSpanFlags(Object tag) {
+            return mText.getSpanFlags(tag);
+        }
+
+        @Override
+        public int nextSpanTransition(int start, int limit, Class type) {
+            return mText.nextSpanTransition(start, limit, type);
+        }
+
+        @Override
+        public int length() {
+            return mText.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return mText.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return mText.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return mText.toString();
+        }
+    };
+}
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index be3a0be..2fe7db4 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -31,6 +31,11 @@
 import android.text.method.LinkMovementMethod;
 import android.text.style.URLSpan;
 import android.util.Patterns;
+import android.view.textclassifier.SystemTextClassifier;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierImpl;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
 import android.widget.TextView;
 
 import org.junit.After;
@@ -38,7 +43,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * LinkifyTest tests {@link Linkify}.
@@ -149,4 +158,55 @@
         assertEquals("android.com should be linkified", 1, spans.length);
         assertEquals("https://android.com", spans[0].getURL());
     }
+
+    @Test
+    public void testAddLinksAsync_useLegacyIfSmartDisabled_localTextClassifier()
+            throws Exception {
+        final TextClassificationConstants settings =
+                TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
+        final TextClassifier classifier = new TextClassifierImpl(mContext, settings);
+        testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
+    }
+
+    @Test
+    public void testAddLinksAsync_useLegacyIfSmartDisabled_systemTextClassifier()
+            throws Exception {
+        final TextClassificationConstants settings =
+                TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
+        final TextClassifier classifier = new SystemTextClassifier(mContext, settings);
+        testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
+    }
+
+    private void testAddLinksAsync_useLegacyIfSmartDisabled(TextClassifier classifier)
+            throws Exception {
+        final int linkMask = Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS;
+        final String string = "example@android.com\n"
+                + "(415) 555-1212\n"
+                + "http://android.com\n"
+                + "100 Android Rd California";
+        final Spannable expected = new SpannableString(string);
+        final Spannable actual = new SpannableString(string);
+
+        Linkify.addLinks(expected, linkMask);  // legacy.
+        Linkify.addLinksAsync(actual, classifier, linkMask).get();
+
+        final URLSpan[] expectedSpans = expected.getSpans(0, expected.length(), URLSpan.class);
+        final TextLinkSpan[] actualSpans = actual.getSpans(0, actual.length(), TextLinkSpan.class);
+        assertEquals(expectedSpans.length, actualSpans.length);
+        final Set<List> expectedLinkSpec = new HashSet<>();
+        final Set<List> actualLinkSpec = new HashSet<>();
+        for (int i = 0; i < expectedSpans.length; i++) {
+            final URLSpan expectedSpan = expectedSpans[i];
+            final TextLinkSpan actualSpan = actualSpans[i];
+            expectedLinkSpec.add(Arrays.asList(
+                    expected.getSpanStart(expectedSpan),
+                    expected.getSpanEnd(expectedSpan),
+                    expectedSpan.getURL()));
+            actualLinkSpec.add(Arrays.asList(
+                    actual.getSpanStart(actualSpan),
+                    actual.getSpanEnd(actualSpan),
+                    actualSpan.getUrl()));
+        }
+        assertEquals(expectedLinkSpec, actualLinkSpec);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index e6ac682..1fd24e3 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -99,6 +99,10 @@
         assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
                 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                 "DummyNonDefaultAutoVoiceIme1");
+        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
+                "DummyDefaultEnKeyboardIme");
+        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
+                "DummyDefaultEnKeyboardIme");
 
         // locale: en_GB
         assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
@@ -106,6 +110,10 @@
         assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
                 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                 "DummyNonDefaultAutoVoiceIme1");
+        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
+                "DummyDefaultEnKeyboardIme");
+        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
+                "DummyDefaultEnKeyboardIme");
 
         // locale: ja_JP
         assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
@@ -113,6 +121,10 @@
         assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
                 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                 "DummyNonDefaultAutoVoiceIme1");
+        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
+                "DummyDefaultEnKeyboardIme");
+        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
+                "DummyDefaultEnKeyboardIme");
     }
 
     @Test
@@ -120,34 +132,49 @@
         // locale: en_US
         assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
                 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
+                "com.android.apps.inputmethod.latin");
 
         // locale: en_GB
         assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
                 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
+                "com.android.apps.inputmethod.latin");
 
         // locale: en_IN
         assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
                 "com.android.apps.inputmethod.hindi",
                 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
+                "com.android.apps.inputmethod.hindi",
+                "com.android.apps.inputmethod.latin");
 
         // locale: hi
         assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
                 "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin",
                 "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
+                "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin");
 
         // locale: ja_JP
         assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
                 "com.android.apps.inputmethod.japanese", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
+                "com.android.apps.inputmethod.japanese");
 
         // locale: zh_CN
         assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
                 "com.android.apps.inputmethod.pinyin", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
+                "com.android.apps.inputmethod.pinyin");
 
         // locale: zh_TW
         // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a
         // fallback IME regardless of the "default" attribute.
         assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
                 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
+        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
+                "com.android.apps.inputmethod.latin");
     }
 
     @Test
@@ -785,6 +812,18 @@
         }
     }
 
+    private void assertDefaultEnabledMinimumImes(final ArrayList<InputMethodInfo> preinstalledImes,
+            final Locale systemLocale, String... expectedImeNames) {
+        final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
+        final String[] actualImeNames = getPackageNames(
+                InputMethodUtils.getDefaultEnabledImes(context, preinstalledImes,
+                        true /* onlyMinimum */));
+        assertEquals(expectedImeNames.length, actualImeNames.length);
+        for (int i = 0; i < expectedImeNames.length; ++i) {
+            assertEquals(expectedImeNames[i], actualImeNames[i]);
+        }
+    }
+
     private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) {
         Parcel p = null;
         try {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index cb049b7..ee8d508 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -1085,6 +1085,49 @@
     }
 
     @Test
+    public void testReadKernelUidCpuActiveTimesLocked_invalidUid() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+        doAnswer(invocation -> {
+            final KernelUidCpuActiveTimeReader.Callback callback =
+                    (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuActiveTime(invalidUid, 1200L);
+            return null;
+        }).when(mKernelUidCpuActiveTimeReader).readDelta(
+                any(KernelUidCpuActiveTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuActiveTime());
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuActiveTimeReader).removeUid(invalidUid);
+    }
+
+    @Test
     public void testReadKernelUidCpuClusterTimesLocked() {
         // PRECONDITIONS
         updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
@@ -1153,6 +1196,97 @@
         }
     }
 
+    @Test
+    public void testReadKernelUidCpuClusterTimesLocked_invalidUid() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesMs = {
+                {4000, 10000},
+                {5000, 1000},
+                {8000, 0}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuClusterTimeReader.Callback callback =
+                    (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuPolicyTime(invalidUid, new long[] {400, 1000});
+            return null;
+        }).when(mKernelUidCpuClusterTimeReader).readDelta(
+                any(KernelUidCpuClusterTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuClusterTimes());
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuClusterTimeReader).removeUid(invalidUid);
+    }
+
+    @Test
+    public void testRemoveUidCpuTimes() {
+        mClocks.realtime = mClocks.uptime = 0;
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(1, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(5, 10, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 100_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 200_000;
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(100, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(3, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 400_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(1, mBatteryStatsImpl.getPendingRemovedUids().size());
+        verify(mKernelUidCpuActiveTimeReader).removeUid(1);
+        verify(mKernelUidCpuActiveTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuClusterTimeReader).removeUid(1);
+        verify(mKernelUidCpuClusterTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuFreqTimeReader).removeUid(1);
+        verify(mKernelUidCpuFreqTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuTimeReader).removeUid(1);
+        verify(mKernelUidCpuTimeReader).removeUidsInRange(5, 10);
+
+        mClocks.realtime = mClocks.uptime = 800_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(0, mBatteryStatsImpl.getPendingRemovedUids().size());
+        verify(mKernelUidCpuActiveTimeReader).removeUid(100);
+        verify(mKernelUidCpuClusterTimeReader).removeUid(100);
+        verify(mKernelUidCpuFreqTimeReader).removeUid(100);
+        verify(mKernelUidCpuTimeReader).removeUid(100);
+
+        verifyNoMoreInteractions(mKernelUidCpuActiveTimeReader, mKernelUidCpuClusterTimeReader,
+                mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
+    }
+
     private void updateTimeBasesLocked(boolean unplugged, int screenState,
             long upTime, long realTime) {
         // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 01ddc15..8cbe5d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -244,13 +244,16 @@
         assertEquals(bi.getScreenState(), Display.STATE_OFF);
     }
 
-    /**
+    /*
      * Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly.
      *
      * Unknown and doze should both be subset of off state
      *
-     * Timeline 0----100----200----310----400------------1000 Unknown         ------- On ------- Off
-     * -------       ---------------------- Doze ----------------
+     * Timeline 0----100----200----310----400------------1000
+     * Unknown         -------
+     * On                     -------
+     * Off             -------       ----------------------
+     * Doze                                ----------------
      */
     @SmallTest
     public void testNoteScreenStateTimersLocked() throws Exception {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index f5fe80c..94492ba 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -43,6 +43,7 @@
         KernelUidCpuActiveTimeReaderTest.class,
         KernelUidCpuClusterTimeReaderTest.class,
         KernelWakelockReaderTest.class,
+        LongSamplingCounterTest.class,
         LongSamplingCounterArrayTest.class,
         PowerCalculatorTest.class,
         PowerProfileTest.class
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
index 06f51c9..28570e8 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -57,7 +57,7 @@
     }
 
     @Test
-    public void testReadDelta() throws Exception {
+    public void testReadDelta() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
 
@@ -104,7 +104,7 @@
     }
 
     @Test
-    public void testReadAbsolute() throws Exception {
+    public void testReadAbsolute() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
 
@@ -128,7 +128,7 @@
     }
 
     @Test
-    public void testReadDelta_malformedData() throws Exception {
+    public void testReadDelta_malformedData() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
         final long[][] times = increaseTime(new long[uids.length][cores]);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
index a6b99c3..67c4e61 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
@@ -145,82 +144,6 @@
     }
 
     @Test
-    public void testReadDelta() throws Exception {
-        final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
-        final int[] uids = {1, 22, 333, 4444, 5555};
-        final long[][] times = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                times[i][j] = uids[i] * freqs[j] * 10;
-            }
-        }
-        when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
-        long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
-        assertArrayEquals(freqs, actualFreqs);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that a second call will only return deltas.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes1 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes1[i][j] = (times[i][j] + uids[i] + freqs[j]) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there won't be a callback if the proc file values didn't change.
-        Mockito.reset(mCallback, mBufferedReader);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that calling with a null callback doesn't result in any crashes
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes2 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes2[i][j] = (newTimes1[i][j] + uids[i] * freqs[j]) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, null);
-
-        // Verify that the readDelta call will only return deltas when
-        // the previous call had null callback.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes3 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes3[i][j] = (newTimes2[i][j] * (uids[i] + freqs[j])) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes3));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
-                    subtract(newTimes3[i], newTimes2[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
     public void testReadDelta_Binary() throws Exception {
         VerifiableCallback cb = new VerifiableCallback();
         final long[] freqs = {110, 123, 145, 167, 289, 997};
@@ -236,7 +159,7 @@
 
         assertArrayEquals(freqs, actualFreqs);
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], times[i]);
         }
@@ -252,7 +175,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], subtract(newTimes1[i], times[i]));
         }
@@ -262,7 +185,7 @@
         cb.clear();
         Mockito.reset(mProcReader);
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         cb.verifyNoMoreInteractions();
 
         // Verify that calling with a null callback doesn't result in any crashes
@@ -275,7 +198,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(null);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(null);
         cb.verifyNoMoreInteractions();
 
         // Verify that the readDelta call will only return deltas when
@@ -289,7 +212,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes3));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], subtract(newTimes3[i], newTimes2[i]));
         }
@@ -336,91 +259,6 @@
         cb.verifyNoMoreInteractions();
     }
 
-    @Test
-    public void testReadDelta_malformedData() throws Exception {
-        final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
-        final int[] uids = {1, 22, 333, 4444, 5555};
-        final long[][] times = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                times[i][j] = uids[i] * freqs[j] * 10;
-            }
-        }
-        when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
-        long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
-        assertArrayEquals(freqs, actualFreqs);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there is no callback if any value in the proc file is -ve.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes1 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes1[i][j] = (times[i][j] + uids[i] + freqs[j]) * 10;
-            }
-        }
-        newTimes1[uids.length - 1][freqs.length - 1] *= -1;
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            if (i == uids.length - 1) {
-                continue;
-            }
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that the internal state was not modified when the proc file had -ve value.
-        Mockito.reset(mCallback, mBufferedReader);
-        for (int i = 0; i < freqs.length; ++i) {
-            newTimes1[uids.length - 1][i] = times[uids.length - 1][i];
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there is no callback if the values in the proc file are decreased.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes2 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes2[i][j] = (newTimes1[i][j] + uids[i] * freqs[j]) * 10;
-            }
-        }
-        newTimes2[uids.length - 1][freqs.length - 1] =
-                newTimes1[uids.length - 1][freqs.length - 1] - 222;
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            if (i == uids.length - 1) {
-                continue;
-            }
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
-                    subtract(newTimes2[i], newTimes1[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that the internal state was not modified when the proc file had decreasing values.
-        Mockito.reset(mCallback, mBufferedReader);
-        for (int i = 0; i < freqs.length; ++i) {
-            newTimes2[uids.length - 1][i] = newTimes1[uids.length - 1][i];
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-    }
-
     private long[] subtract(long[] a1, long[] a2) {
         long[] val = new long[a1.length];
         for (int i = 0; i < val.length; ++i) {
@@ -438,21 +276,6 @@
         return sb.toString();
     }
 
-    private String[] getUidTimesLines(int[] uids, long[][] times) {
-        final String[] lines = new String[uids.length + 1];
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < uids.length; ++i) {
-            sb.setLength(0);
-            sb.append(uids[i] + ":");
-            for (int j = 0; j < times[i].length; ++j) {
-                sb.append(" " + times[i][j] / 10);
-            }
-            lines[i] = sb.toString();
-        }
-        lines[uids.length] = null;
-        return lines;
-    }
-
     private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
         int size = (1 + uids.length + uids.length * times[0].length) * 4;
         ByteBuffer buf = ByteBuffer.allocate(size);
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
new file mode 100644
index 0000000..853bf8a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+
+import static com.android.internal.os.BatteryStatsImpl.LongSamplingCounter;
+import static com.android.internal.os.BatteryStatsImpl.TimeBase;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link LongSamplingCounter}.
+ *
+ * To run the tests, use
+ *
+ * bit FrameworksCoreTests:com.android.internal.os.LongSamplingCounterTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LongSamplingCounterTest {
+
+    private static final long COUNT = 1111;
+    private static final long CURRENT_COUNT = 5555;
+
+    @Mock
+    private TimeBase mTimeBase;
+    private LongSamplingCounter mCounter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mCounter = new LongSamplingCounter(mTimeBase);
+        Mockito.reset(mTimeBase);
+    }
+
+    @Test
+    public void testReadWriteParcel() {
+        final Parcel parcel = Parcel.obtain();
+        updateCounts(COUNT, CURRENT_COUNT);
+        mCounter.writeToParcel(parcel);
+        parcel.setDataPosition(0);
+
+        // Now clear counterArray and verify values are read from parcel correctly.
+        updateCounts(0, 0);
+
+        mCounter = new LongSamplingCounter(mTimeBase, parcel);
+        assertEquals(COUNT, mCounter.mCount);
+        assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+        parcel.recycle();
+    }
+
+    @Test
+    public void testReadWriteSummaryParcel() {
+        final Parcel parcel = Parcel.obtain();
+        updateCounts(COUNT, CURRENT_COUNT);
+        mCounter.writeSummaryFromParcelLocked(parcel);
+        parcel.setDataPosition(0);
+
+        // Now clear counterArray and verify values are read from parcel correctly.
+        updateCounts(0, 0);
+
+        mCounter.readSummaryFromParcelLocked(parcel);
+        assertEquals(COUNT, mCounter.mCount);
+        parcel.recycle();
+    }
+
+    @Test
+    public void testOnTimeStarted() {
+        updateCounts(COUNT, CURRENT_COUNT);
+        mCounter.onTimeStarted(0, 0, 0);
+        assertEquals(COUNT, mCounter.mCount);
+        assertEquals(COUNT, mCounter.mUnpluggedCount);
+    }
+
+    @Test
+    public void testOnTimeStopped() {
+        updateCounts(COUNT, CURRENT_COUNT);
+        mCounter.onTimeStopped(0, 0, 0);
+        assertEquals(COUNT, mCounter.mCount);
+    }
+
+    @Test
+    public void testAddCountLocked() {
+        updateCounts(0, 0);
+        assertEquals(0, mCounter.getCountLocked(0));
+        when(mTimeBase.isRunning()).thenReturn(true);
+        mCounter.addCountLocked(111);
+        assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(111, mCounter.mCurrentCount);
+        mCounter.addCountLocked(222);
+        assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(333, mCounter.mCurrentCount);
+
+        when(mTimeBase.isRunning()).thenReturn(false);
+        mCounter.addCountLocked(456);
+        assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(789, mCounter.mCurrentCount);
+
+        mCounter.addCountLocked(444, true);
+        assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(1233, mCounter.mCurrentCount);
+        mCounter.addCountLocked(567, false);
+        assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(1800, mCounter.mCurrentCount);
+    }
+
+    @Test
+    public void testUpdate() {
+        updateCounts(0, 0);
+        assertEquals(0, mCounter.getCountLocked(0));
+        when(mTimeBase.isRunning()).thenReturn(true);
+        mCounter.update(111);
+        assertEquals(111, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(111, mCounter.mCurrentCount);
+        mCounter.update(333);
+        assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(333, mCounter.mCurrentCount);
+
+        when(mTimeBase.isRunning()).thenReturn(false);
+        mCounter.update(789);
+        assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(789, mCounter.mCurrentCount);
+        mCounter.update(100);
+        assertEquals(333, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(100, mCounter.mCurrentCount);
+
+        mCounter.update(544, true);
+        assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(544, mCounter.mCurrentCount);
+        mCounter.update(1544, false);
+        assertEquals(777, mCounter.getCountLocked(STATS_SINCE_CHARGED));
+        assertEquals(1544, mCounter.mCurrentCount);
+    }
+
+    @Test
+    public void testReset() {
+        updateCounts(COUNT, CURRENT_COUNT);
+        // Test with detachIfReset=false
+        mCounter.reset(false /* detachIfReset */);
+        assertEquals(0, mCounter.mCount);
+        assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+        verifyZeroInteractions(mTimeBase);
+
+        updateCounts(COUNT, CURRENT_COUNT);
+        // Test with detachIfReset=true
+        mCounter.reset(true /* detachIfReset */);
+        assertEquals(0, mCounter.mCount);
+        assertEquals(CURRENT_COUNT, mCounter.mCurrentCount);
+        verify(mTimeBase).remove(mCounter);
+        verifyNoMoreInteractions(mTimeBase);
+    }
+
+    @Test
+    public void testDetach() {
+        mCounter.detach();
+        verify(mTimeBase).remove(mCounter);
+        verifyNoMoreInteractions(mTimeBase);
+    }
+
+    private void updateCounts(long total, long current) {
+        mCounter.mCount = total;
+        mCounter.mCurrentCount = current;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 6d5c7e774..36e54ad 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.location.gnssmetrics.GnssMetrics;
 import java.util.ArrayList;
+import java.util.Queue;
 import java.util.concurrent.Future;
 
 /**
@@ -66,6 +67,10 @@
         return mScreenState;
     }
 
+    public Queue<UidToRemove> getPendingRemovedUids() {
+        return mPendingRemovedUids;
+    }
+
     public boolean isOnBattery() {
         return mForceOnBattery ? true : super.isOnBattery();
     }
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 002df88..518cadd 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.policy;
 
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -27,7 +26,6 @@
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.ActionMode;
@@ -44,7 +42,6 @@
  */
 @SmallTest
 @Presubmit
-@FlakyTest(detail = "Promote to presubmit once monitored to be stable.")
 @RunWith(AndroidJUnit4.class)
 public final class PhoneWindowTest {
 
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 374d0d0..3c1526b 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 6fe19a2..c88a722 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -16,6 +16,7 @@
 
 package android.privacy;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -72,27 +73,27 @@
         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(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(1, encoder.encodeBoolean(true)[0]);
-        assertEquals(1, encoder.encodeBoolean(true)[0]);
-        assertEquals(1, encoder.encodeBoolean(true)[0]);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
 
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(1, 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(1, encoder.encodeBoolean(false)[0]);
-        assertEquals(1, encoder.encodeBoolean(false)[0]);
+        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(0, encoder.encodeBoolean(false)[0]);
 
         // Test if IRR returns original result when f = 0
         final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
@@ -128,6 +129,31 @@
     }
 
     @Test
+    public void testLongitudinalReportingInsecureEncoder_setSeedCorrectly() throws Exception {
+        final int n = 10000;
+        final double f = 0.35;
+        final double expectedTrueSum = n * f;
+        final double valueRange = 5 * Math.sqrt(n * f * (1 - f));
+        int trueSum = 0;
+        for (int i = 0; i < n; i++) {
+            final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
+                    "encoder" + i,  // encoderId
+                    f,  // probabilityF
+                    0,  // probabilityP
+                    1);  // probabilityQ
+            final LongitudinalReportingEncoder encoder
+                    = LongitudinalReportingEncoder.createInsecureEncoderForTest(config);
+            boolean encodedFalse = encoder.encodeBoolean(false)[0] > 0;
+            if (encodedFalse) {
+                trueSum += 1;
+            }
+        }
+        // Total number of true(s) should be around the mean (10000 * 0.35)
+        assertThat((double) trueSum).isLessThan(expectedTrueSum + valueRange);
+        assertThat((double) trueSum).isAtLeast(expectedTrueSum - valueRange);
+    }
+
+    @Test
     public void testLongitudinalReportingEncoder_basicPRRTest() throws Exception {
         // Should always return original value when p = 0
         for (int i = 0; i < 10; i++) {
@@ -290,8 +316,8 @@
             }
         }
         // Total number of true(s) should be around the mean (1000 * 0.8)
-        assertTrue(trueSum1 < expectedTrueSum1 + valueRange1);
-        assertTrue(trueSum1 > expectedTrueSum1 - valueRange1);
+        assertThat((double) trueSum1).isLessThan(expectedTrueSum1 + valueRange1);
+        assertThat((double) trueSum1).isAtLeast(expectedTrueSum1 - valueRange1);
 
         // Confirm if PRR randomizer is working correctly
         final int n2 = 1000;
@@ -314,8 +340,8 @@
             }
         }
         // Total number of true(s) should be around the mean (1000 * 0.2)
-        assertTrue(trueSum2 < expectedTrueSum2 + valueRange2);
-        assertTrue(trueSum2 > expectedTrueSum2 - valueRange2);
+        assertThat((double) trueSum2).isLessThan(expectedTrueSum2 + valueRange2);
+        assertThat((double) trueSum2).isAtLeast(expectedTrueSum2 - valueRange2);
     }
 
     @Test
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index fa0343d..71bd8f1 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -79,11 +79,11 @@
     public void testRapporEncoder_IRRWithPRR() throws Exception {
         int numBits = 8;
         final long inputValue = 254L;
-        final long prrValue = 250L;
-        final long prrAndIrrValue = 244L;
+        final long expectedPrrValue = 126L;
+        final long expectedPrrAndIrrValue = 79L;
 
         final RapporConfig config1 = new RapporConfig(
-                "Foo", // encoderId
+                "Foo2", // encoderId
                 numBits, // numBits,
                 0.25, // probabilityF
                 0, // probabilityP
@@ -93,12 +93,12 @@
         // Use insecure encoder here as we want to get the exact output.
         final RapporEncoder encoder1 = RapporEncoder.createInsecureEncoderForTest(config1);
         // Verify that PRR is working as expected.
-        assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
+        assertEquals(expectedPrrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
         assertTrue(encoder1.isInsecureEncoderForTest());
 
         // Verify that IRR is working as expected.
         final RapporConfig config2 = new RapporConfig(
-                "Foo", // encoderId
+                "Foo2", // encoderId
                 numBits, // numBits,
                 0, // probabilityF
                 0.3, // probabilityP
@@ -107,11 +107,12 @@
                 2); // numBloomHashes
         // Use insecure encoder here as we want to get the exact output.
         final RapporEncoder encoder2 = RapporEncoder.createInsecureEncoderForTest(config2);
-        assertEquals(prrAndIrrValue, toLong(encoder2.encodeBits(toBytes(prrValue))));
+        assertEquals(expectedPrrAndIrrValue,
+                toLong(encoder2.encodeBits(toBytes(expectedPrrValue))));
 
         // Test that end-to-end is the result of PRR + IRR.
         final RapporConfig config3 = new RapporConfig(
-                "Foo", // encoderId
+                "Foo2", // encoderId
                 numBits, // numBits,
                 0.25, // probabilityF
                 0.3, // probabilityP
@@ -120,7 +121,7 @@
                 2); // numBloomHashes
         final RapporEncoder encoder3 = RapporEncoder.createInsecureEncoderForTest(config3);
         // Verify that PRR is working as expected.
-        assertEquals(prrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
+        assertEquals(expectedPrrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
     }
 
     @Test
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 282b001..933e54e 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 import junit.framework.TestCase;
 
 import android.os.SystemProperties;
@@ -141,4 +144,48 @@
         } catch (NullPointerException npe) {
         }
     }
+
+    @SmallTest
+    public void testCallbacks() {
+        // Latches are not really necessary, but are easy to use.
+        final CountDownLatch wait1 = new CountDownLatch(1);
+        final CountDownLatch wait2 = new CountDownLatch(1);
+
+        Runnable r1 = new Runnable() {
+            boolean done = false;
+            @Override
+            public void run() {
+                if (done) {
+                    return;
+                }
+                done = true;
+
+                wait1.countDown();
+                throw new RuntimeException("test");
+            }
+        };
+
+        Runnable r2 = new Runnable() {
+            @Override
+            public void run() {
+                wait2.countDown();
+            }
+        };
+
+        SystemProperties.addChangeCallback(r1);
+        SystemProperties.addChangeCallback(r2);
+
+        SystemProperties.reportSyspropChanged();
+
+        try {
+            assertTrue(wait1.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("InterruptedException");
+        }
+        try {
+            assertTrue(wait2.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("InterruptedException");
+        }
+    }
 }
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index be0372d..bacddf14 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -25,42 +25,50 @@
   <hidden-api-whitelisted-app package="android.car.input.service" />
   <hidden-api-whitelisted-app package="android.car.usb.handler" />
   <hidden-api-whitelisted-app package="android.ext.services" />
-  <hidden-api-whitelisted-app package="android.ext.shared" />
+  <hidden-api-whitelisted-app package="com.android.apps.tag" />
   <hidden-api-whitelisted-app package="com.android.backupconfirm" />
+  <hidden-api-whitelisted-app package="com.android.basicsmsreceiver" />
   <hidden-api-whitelisted-app package="com.android.bluetooth" />
   <hidden-api-whitelisted-app package="com.android.bluetoothdebug" />
   <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" />
+  <hidden-api-whitelisted-app package="com.android.bookmarkprovider" />
   <hidden-api-whitelisted-app package="com.android.calllogbackup" />
+  <hidden-api-whitelisted-app package="com.android.camera" />
   <hidden-api-whitelisted-app package="com.android.captiveportallogin" />
   <hidden-api-whitelisted-app package="com.android.car" />
+  <hidden-api-whitelisted-app package="com.android.car.dialer" />
   <hidden-api-whitelisted-app package="com.android.car.hvac" />
   <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" />
   <hidden-api-whitelisted-app package="com.android.car.media" />
   <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" />
+  <hidden-api-whitelisted-app package="com.android.car.messenger" />
+  <hidden-api-whitelisted-app package="com.android.car.overview" />
   <hidden-api-whitelisted-app package="com.android.car.radio" />
   <hidden-api-whitelisted-app package="com.android.car.settings" />
+  <hidden-api-whitelisted-app package="com.android.car.stream" />
   <hidden-api-whitelisted-app package="com.android.car.systemupdater" />
   <hidden-api-whitelisted-app package="com.android.car.trust" />
   <hidden-api-whitelisted-app package="com.android.carrierconfig" />
   <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" />
   <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" />
   <hidden-api-whitelisted-app package="com.android.certinstaller" />
+  <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
   <hidden-api-whitelisted-app package="com.android.customlocale2" />
   <hidden-api-whitelisted-app package="com.android.defcontainer" />
   <hidden-api-whitelisted-app package="com.android.documentsui" />
+  <hidden-api-whitelisted-app package="com.android.dreams.basic" />
   <hidden-api-whitelisted-app package="com.android.egg" />
-  <hidden-api-whitelisted-app package="com.android.email.policy" />
   <hidden-api-whitelisted-app package="com.android.emergency" />
   <hidden-api-whitelisted-app package="com.android.externalstorage" />
   <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" />
   <hidden-api-whitelisted-app package="com.android.gallery" />
   <hidden-api-whitelisted-app package="com.android.hotspot2" />
-  <hidden-api-whitelisted-app package="com.android.inputdevices" />
   <hidden-api-whitelisted-app package="com.android.keychain" />
   <hidden-api-whitelisted-app package="com.android.location.fused" />
   <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
   <hidden-api-whitelisted-app package="com.android.mms.service" />
   <hidden-api-whitelisted-app package="com.android.mtp" />
+  <hidden-api-whitelisted-app package="com.android.musicfx" />
   <hidden-api-whitelisted-app package="com.android.nfc" />
   <hidden-api-whitelisted-app package="com.android.osu" />
   <hidden-api-whitelisted-app package="com.android.packageinstaller" />
@@ -70,12 +78,14 @@
   <hidden-api-whitelisted-app package="com.android.printservice.recommendation" />
   <hidden-api-whitelisted-app package="com.android.printspooler" />
   <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
+  <hidden-api-whitelisted-app package="com.android.providers.calendar" />
   <hidden-api-whitelisted-app package="com.android.providers.contacts" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" />
   <hidden-api-whitelisted-app package="com.android.providers.media" />
   <hidden-api-whitelisted-app package="com.android.providers.settings" />
   <hidden-api-whitelisted-app package="com.android.providers.telephony" />
+  <hidden-api-whitelisted-app package="com.android.providers.tv" />
   <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
   <hidden-api-whitelisted-app package="com.android.provision" />
   <hidden-api-whitelisted-app package="com.android.proxyhandler" />
@@ -87,10 +97,15 @@
   <hidden-api-whitelisted-app package="com.android.settings" />
   <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" />
   <hidden-api-whitelisted-app package="com.android.shell" />
+  <hidden-api-whitelisted-app package="com.android.smspush" />
+  <hidden-api-whitelisted-app package="com.android.spare_parts" />
+  <hidden-api-whitelisted-app package="com.android.statementservice" />
   <hidden-api-whitelisted-app package="com.android.stk" />
+  <hidden-api-whitelisted-app package="com.android.storagemanager" />
   <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" />
   <hidden-api-whitelisted-app package="com.android.systemui" />
-  <hidden-api-whitelisted-app package="com.android.systemui.theme.dark" />
+  <hidden-api-whitelisted-app package="com.android.systemui.plugins" />
+  <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.timezone.updater" />
   <hidden-api-whitelisted-app package="com.android.traceur" />
   <hidden-api-whitelisted-app package="com.android.tv.settings" />
@@ -99,5 +114,5 @@
   <hidden-api-whitelisted-app package="com.android.wallpaperbackup" />
   <hidden-api-whitelisted-app package="com.android.wallpapercropper" />
   <hidden-api-whitelisted-app package="com.googlecode.android_scripting" />
+  <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
 </config>
-
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b455419..c6a6113 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -325,6 +325,7 @@
         <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+        <permission name="android.permission.START_TASKS_FROM_RECENTS" />
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 52d29e3..e8ede94 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1669,6 +1669,8 @@
         if (mColorSpace == null) {
             if (nativeIsSRGB(mNativePtr)) {
                 mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+            } else if (getConfig() == Config.HARDWARE && nativeIsSRGBLinear(mNativePtr)) {
+                mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
             } else {
                 float[] xyz = new float[9];
                 float[] params = new float[7];
@@ -2092,5 +2094,6 @@
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
     private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
     private static native boolean nativeIsSRGB(long nativePtr);
+    private static native boolean nativeIsSRGBLinear(long nativePtr);
     private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);
 }
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d925441..b0bc102 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1490,6 +1490,10 @@
      * across the top of the bitmap from left to right. A more general version of this method is
      * drawVertices().
      *
+     * Prior to API level {@value Build.VERSION_CODES#P} vertOffset and colorOffset were ignored,
+     * effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
+     * these parameters will be respected.
+     *
      * @param bitmap The bitmap to draw using the mesh
      * @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
      * @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 88701fa..5ca0ad6 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -24,9 +24,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RawRes;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
 import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
@@ -34,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
+import android.os.Build;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.DisplayMetrics;
@@ -58,6 +60,9 @@
  *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
  */
 public final class ImageDecoder implements AutoCloseable {
+    /** @hide **/
+    public static int sApiLevel;
+
     /**
      *  Source of the encoded image data.
      */
@@ -98,7 +103,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return nCreate(mData, mOffset, mLength);
+            return nCreate(mData, mOffset, mLength, this);
         }
     }
 
@@ -113,9 +118,9 @@
             if (!mBuffer.isDirect() && mBuffer.hasArray()) {
                 int offset = mBuffer.arrayOffset() + mBuffer.position();
                 int length = mBuffer.limit() - mBuffer.position();
-                return nCreate(mBuffer.array(), offset, length);
+                return nCreate(mBuffer.array(), offset, length, this);
             }
-            return nCreate(mBuffer, mBuffer.position(), mBuffer.limit());
+            return nCreate(mBuffer, mBuffer.position(), mBuffer.limit(), this);
         }
     }
 
@@ -152,7 +157,7 @@
                     throw new FileNotFoundException(mUri.toString());
                 }
 
-                return createFromStream(is, true);
+                return createFromStream(is, true, this);
             }
 
             final FileDescriptor fd = assetFd.getFileDescriptor();
@@ -162,9 +167,9 @@
             try {
                 try {
                     Os.lseek(fd, offset, SEEK_SET);
-                    decoder = nCreate(fd);
+                    decoder = nCreate(fd, this);
                 } catch (ErrnoException e) {
-                    decoder = createFromStream(new FileInputStream(fd), true);
+                    decoder = createFromStream(new FileInputStream(fd), true, this);
                 }
             } finally {
                 if (decoder == null) {
@@ -178,18 +183,19 @@
     }
 
     @NonNull
-    private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
+    private static ImageDecoder createFromFile(@NonNull File file,
+            @NonNull Source source) throws IOException {
         FileInputStream stream = new FileInputStream(file);
         FileDescriptor fd = stream.getFD();
         try {
             Os.lseek(fd, 0, SEEK_CUR);
         } catch (ErrnoException e) {
-            return createFromStream(stream, true);
+            return createFromStream(stream, true, source);
         }
 
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(fd);
+            decoder = nCreate(fd, source);
         } finally {
             if (decoder == null) {
                 IoUtils.closeQuietly(stream);
@@ -203,12 +209,12 @@
 
     @NonNull
     private static ImageDecoder createFromStream(@NonNull InputStream is,
-            boolean closeInputStream) throws IOException {
+            boolean closeInputStream, Source source) throws IOException {
         // Arbitrary size matches BitmapFactory.
         byte[] storage = new byte[16 * 1024];
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(is, storage);
+            decoder = nCreate(is, storage, source);
         } finally {
             if (decoder == null) {
                 if (closeInputStream) {
@@ -256,7 +262,7 @@
                 }
                 InputStream is = mInputStream;
                 mInputStream = null;
-                return createFromStream(is, false);
+                return createFromStream(is, false, this);
             }
         }
     }
@@ -295,25 +301,13 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            ImageDecoder decoder = null;
             synchronized (this) {
                 if (mAssetInputStream == null) {
                     throw new IOException("Cannot reuse AssetInputStreamSource");
                 }
                 AssetInputStream ais = mAssetInputStream;
                 mAssetInputStream = null;
-                try {
-                    long asset = ais.getNativeAsset();
-                    decoder = nCreate(asset);
-                } finally {
-                    if (decoder == null) {
-                        IoUtils.closeQuietly(ais);
-                    } else {
-                        decoder.mInputStream = ais;
-                        decoder.mOwnsInputStream = true;
-                    }
-                }
-                return decoder;
+                return createFromAsset(ais, this);
             }
         }
     }
@@ -337,31 +331,54 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            // This is just used in order to access the underlying Asset and
-            // keep it alive. FIXME: Can we skip creating this object?
-            InputStream is = null;
-            ImageDecoder decoder = null;
             TypedValue value = new TypedValue();
-            try {
-                is = mResources.openRawResource(mResId, value);
+            // This is just used in order to access the underlying Asset and
+            // keep it alive.
+            InputStream is = mResources.openRawResource(mResId, value);
 
-                if (value.density == TypedValue.DENSITY_DEFAULT) {
-                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
-                } else if (value.density != TypedValue.DENSITY_NONE) {
-                    mResDensity = value.density;
-                }
-
-                long asset = ((AssetInputStream) is).getNativeAsset();
-                decoder = nCreate(asset);
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(is);
-                } else {
-                    decoder.mInputStream = is;
-                    decoder.mOwnsInputStream = true;
-                }
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                mResDensity = value.density;
             }
-            return decoder;
+
+            return createFromAsset((AssetInputStream) is, this);
+        }
+    }
+
+    /**
+     *  ImageDecoder will own the AssetInputStream.
+     */
+    private static ImageDecoder createFromAsset(AssetInputStream ais,
+            Source source) throws IOException {
+        ImageDecoder decoder = null;
+        try {
+            long asset = ais.getNativeAsset();
+            decoder = nCreate(asset, source);
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(ais);
+            } else {
+                decoder.mInputStream = ais;
+                decoder.mOwnsInputStream = true;
+            }
+        }
+        return decoder;
+    }
+
+    private static class AssetSource extends Source {
+        AssetSource(@NonNull AssetManager assets, @NonNull String fileName) {
+            mAssets = assets;
+            mFileName = fileName;
+        }
+
+        private final AssetManager mAssets;
+        private final String mFileName;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            InputStream is = mAssets.open(mFileName);
+            return createFromAsset((AssetInputStream) is, this);
         }
     }
 
@@ -374,7 +391,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return createFromFile(mFile);
+            return createFromFile(mFile, this);
         }
     }
 
@@ -417,9 +434,10 @@
         }
     };
 
-    /**
-     *  Thrown if the provided data is incomplete.
+    /** @removed
+     * @deprecated Subsumed by {@link #DecodeException}.
      */
+    @java.lang.Deprecated
     public static class IncompleteException extends IOException {};
 
     /**
@@ -439,24 +457,102 @@
 
     };
 
-    /**
-     *  An Exception was thrown reading the {@link Source}.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
      */
+    @java.lang.Deprecated
     public static final int ERROR_SOURCE_EXCEPTION  = 1;
 
-    /**
-     *  The encoded data was incomplete.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
      */
+    @java.lang.Deprecated
     public static final int ERROR_SOURCE_INCOMPLETE = 2;
 
-    /**
-     *  The encoded data contained an error.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
      */
+    @java.lang.Deprecated
     public static final int ERROR_SOURCE_ERROR      = 3;
 
-    @Retention(SOURCE)
-    @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
-    public @interface Error {};
+    /**
+     *  Information about an interrupted decode.
+     */
+    public static final class DecodeException extends IOException {
+        /**
+         *  An Exception was thrown reading the {@link Source}.
+         */
+        public static final int SOURCE_EXCEPTION  = 1;
+
+        /**
+         *  The encoded data was incomplete.
+         */
+        public static final int SOURCE_INCOMPLETE = 2;
+
+        /**
+         *  The encoded data contained an error.
+         */
+        public static final int SOURCE_MALFORMED_DATA      = 3;
+
+        /** @hide **/
+        @Retention(SOURCE)
+        @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA },
+                prefix = {"SOURCE_"})
+        public @interface Error {};
+
+        @Error final int mError;
+        @NonNull final Source mSource;
+
+        DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
+            super(errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         * Private method called by JNI.
+         */
+        @SuppressWarnings("unused")
+        DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
+                @NonNull Source source) {
+            super(msg + errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         *  Retrieve the reason that decoding was interrupted.
+         *
+         *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
+         *  {@link java.lang.Throwable} can be retrieved with
+         *  {@link java.lang.Throwable#getCause}.</p>
+         */
+        @Error
+        public int getError() {
+            return mError;
+        }
+
+        /**
+         *  Retrieve the {@link Source} that was interrupted.
+         */
+        @NonNull
+        public Source getSource() {
+            return mSource;
+        }
+
+        private static String errorMessage(@Error int error, @Nullable Throwable cause) {
+            switch (error) {
+                case SOURCE_EXCEPTION:
+                    return "Exception in input: " + cause;
+                case SOURCE_INCOMPLETE:
+                    return "Input was incomplete.";
+                case SOURCE_MALFORMED_DATA:
+                    return "Input contained an error.";
+                default:
+                    return "";
+            }
+        }
+    }
 
     /**
      *  Optional listener supplied to the ImageDecoder.
@@ -472,13 +568,12 @@
          *  optionally finish the rest of the decode/creation process to create
          *  a partial {@link Drawable}/{@link Bitmap}.
          *
-         *  @param error indicating what interrupted the decode.
-         *  @param source that had the error.
+         *  @param e containing information about the decode interruption.
          *  @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}.
+         *      decode and throw {@code e}.
          */
-        public boolean onPartialImage(@Error int error, @NonNull Source source);
+        boolean onPartialImage(@NonNull DecodeException e);
     };
 
     // Fields
@@ -543,17 +638,15 @@
     }
 
     /**
-     * Create a new {@link Source} from an asset.
-     * @hide
+     * Create a new {@link Source} from a resource.
      *
      * @param res the {@link Resources} object containing the image data.
      * @param resId resource ID of the image data.
-     *      // FIXME: Can be an @DrawableRes?
      * @return a new Source object, which can be passed to
      *      {@link #decodeDrawable} or {@link #decodeBitmap}.
      */
     @NonNull
-    public static Source createSource(@NonNull Resources res, @RawRes int resId)
+    public static Source createSource(@NonNull Resources res, int resId)
     {
         return new ResourceSource(res, resId);
     }
@@ -584,6 +677,14 @@
     }
 
     /**
+     * Create a new {@link Source} from a file in the "assets" directory.
+     */
+    @NonNull
+    public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
+        return new AssetSource(assets, fileName);
+    }
+
+    /**
      * Create a new {@link Source} from a byte array.
      *
      * @param data byte array of compressed image data.
@@ -647,6 +748,7 @@
      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
      * @hide
      */
+    @TestApi
     public static Source createSource(Resources res, InputStream is, int density) {
         return new InputStreamSource(res, is, density);
     }
@@ -1067,14 +1169,8 @@
     @NonNull
     private Bitmap decodeBitmapInternal() throws IOException {
         checkState();
-        // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
-        // exists
-        ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
-        // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
-        // exists.
-        ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
-        return nDecodeBitmap(mNativePtr, partialImagePtr,
-                postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
+        return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
+                mDesiredWidth, mDesiredHeight, mCropRect,
                 mMutable, mAllocator, mRequireUnpremultiplied,
                 mConserveMemory, mDecodeAsAlphaMask);
     }
@@ -1246,17 +1342,19 @@
             return srcDensity;
         }
 
-        // downscale the bitmap if the asset has a higher density than the default
+        // For P and above, only resize if it would be a downscale. Scale up prior
+        // to P in case the app relies on the Bitmap's size without considering density.
         final int dstDensity = src.computeDstDensity();
-        if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
-            float scale = (float) dstDensity / srcDensity;
-            int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
-            int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-            decoder.setResize(scaledWidth, scaledHeight);
-            return dstDensity;
+        if (srcDensity == Bitmap.DENSITY_NONE || srcDensity == dstDensity
+                || (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P)) {
+            return srcDensity;
         }
 
-        return srcDensity;
+        float scale = (float) dstDensity / srcDensity;
+        int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
+        int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
+        decoder.setResize(scaledWidth, scaledHeight);
+        return dstDensity;
     }
 
     @NonNull
@@ -1288,23 +1386,28 @@
      * Private method called by JNI.
      */
     @SuppressWarnings("unused")
-    private boolean onPartialImage(@Error int error) {
-        return mOnPartialImageListener.onPartialImage(error, mSource);
+    private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
+            throws DecodeException {
+        DecodeException exception = new DecodeException(error, cause, mSource);
+        if (mOnPartialImageListener == null
+                || !mOnPartialImageListener.onPartialImage(exception)) {
+            throw exception;
+        }
     }
 
-    private static native ImageDecoder nCreate(long asset) throws IOException;
-    private static native ImageDecoder nCreate(ByteBuffer buffer,
-                                               int position,
-                                               int limit) throws IOException;
-    private static native ImageDecoder nCreate(byte[] data, int offset,
-                                               int length) throws IOException;
-    private static native ImageDecoder nCreate(InputStream is, byte[] storage);
+    private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
+    private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
+                                               int limit, Source src) throws IOException;
+    private static native ImageDecoder nCreate(byte[] data, int offset, int length,
+                                               Source src) throws IOException;
+    private static native ImageDecoder nCreate(InputStream is, byte[] storage,
+                                               Source src) throws IOException;
     // The fd must be seekable.
-    private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
+    private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
     @NonNull
     private static native Bitmap nDecodeBitmap(long nativePtr,
-            @Nullable ImageDecoder partialImageListener,
-            @Nullable ImageDecoder postProcessor,
+            @NonNull ImageDecoder decoder,
+            boolean doPostProcess,
             int width, int height,
             @Nullable Rect cropRect, boolean mutable,
             int allocator, boolean requireUnpremul,
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 9ac94d8..d01ff6f 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -209,6 +209,8 @@
         public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
             mPicture = pict;
+            // Disable bitmap density scaling. This matches DisplayListCanvas.
+            mDensity = 0;
         }
 
         @Override
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index c0f4920..457e4aa 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -261,6 +261,12 @@
         if (repeatCount != REPEAT_UNDEFINED) {
             this.setRepeatCount(repeatCount);
         }
+
+        boolean autoStart = a.getBoolean(
+                R.styleable.AnimatedImageDrawable_autoStart, false);
+        if (autoStart && mState.mNativePtr != 0) {
+            this.start();
+        }
     }
 
     /**
@@ -292,8 +298,7 @@
         mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect),
                 inputStream, afd);
 
-        // FIXME: Use the right size for the native allocation.
-        long nativeSize = 200;
+        final long nativeSize = nNativeByteSize(mState.mNativePtr);
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(mState, mState.mNativePtr);
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index 1aa6cf6..d63feb01 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -26,6 +26,9 @@
   bool has_type_separator = false;
   const char* start = str.data();
   const char* end = start + str.size();
+  if (start[0] == '@') {
+      start++;
+  }
   const char* current = start;
   while (current != end) {
     if (out_type->size() == 0 && *current == '/') {
diff --git a/libs/androidfw/tests/ResourceUtils_test.cpp b/libs/androidfw/tests/ResourceUtils_test.cpp
index b64a884..a02f166 100644
--- a/libs/androidfw/tests/ResourceUtils_test.cpp
+++ b/libs/androidfw/tests/ResourceUtils_test.cpp
@@ -27,23 +27,48 @@
   EXPECT_EQ("string", type);
   EXPECT_EQ("foo", entry);
 
+  ASSERT_TRUE(ExtractResourceName("@android:string/foo", &package, &type, &entry));
+  EXPECT_EQ("android", package);
+  EXPECT_EQ("string", type);
+  EXPECT_EQ("foo", entry);
+
   ASSERT_TRUE(ExtractResourceName("string/foo", &package, &type, &entry));
   EXPECT_EQ("", package);
   EXPECT_EQ("string", type);
   EXPECT_EQ("foo", entry);
 
+  ASSERT_TRUE(ExtractResourceName("@string/foo", &package, &type, &entry));
+  EXPECT_EQ("", package);
+  EXPECT_EQ("string", type);
+  EXPECT_EQ("foo", entry);
+
   ASSERT_TRUE(ExtractResourceName("foo", &package, &type, &entry));
   EXPECT_EQ("", package);
   EXPECT_EQ("", type);
   EXPECT_EQ("foo", entry);
 
+  ASSERT_TRUE(ExtractResourceName("@foo", &package, &type, &entry));
+  EXPECT_EQ("", package);
+  EXPECT_EQ("", type);
+  EXPECT_EQ("foo", entry);
+
   ASSERT_TRUE(ExtractResourceName("android:foo", &package, &type, &entry));
   EXPECT_EQ("android", package);
   EXPECT_EQ("", type);
   EXPECT_EQ("foo", entry);
 
+//  ASSERT_TRUE(ExtractResourceName("@android:foo", &package, &type, &entry));
+//  EXPECT_EQ("android", package);
+//  EXPECT_EQ("", type);
+//  EXPECT_EQ("foo", entry);
+
   EXPECT_FALSE(ExtractResourceName(":string/foo", &package, &type, &entry));
+
+  EXPECT_FALSE(ExtractResourceName("@:string/foo", &package, &type, &entry));
+
   EXPECT_FALSE(ExtractResourceName("/foo", &package, &type, &entry));
+
+  EXPECT_FALSE(ExtractResourceName("@/foo", &package, &type, &entry));
 }
 
 }  // namespace android
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index e416287..40cc73a 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -16,6 +16,8 @@
 
 #include <DeviceInfo.h>
 
+#include "Properties.h"
+
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 
@@ -29,6 +31,19 @@
 namespace android {
 namespace uirenderer {
 
+static constexpr android::DisplayInfo sDummyDisplay {
+        1080,   // w
+        1920,   // h
+        320.0,  // xdpi
+        320.0,  // ydpi
+        60.0,   // fps
+        2.0,    // density
+        0,      // orientation
+        false,  // secure?
+        0,      // appVsyncOffset
+        0,      // presentationDeadline
+};
+
 static DeviceInfo* sDeviceInfo = nullptr;
 static std::once_flag sInitializedFlag;
 
@@ -47,20 +62,26 @@
 void DeviceInfo::initialize(int maxTextureSize) {
     std::call_once(sInitializedFlag, [maxTextureSize]() {
         sDeviceInfo = new DeviceInfo();
-        sDeviceInfo->loadDisplayInfo();
+        sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo();
         sDeviceInfo->mMaxTextureSize = maxTextureSize;
     });
 }
 
 void DeviceInfo::load() {
-    loadDisplayInfo();
+    mDisplayInfo = queryDisplayInfo();
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
 }
 
-void DeviceInfo::loadDisplayInfo() {
+DisplayInfo DeviceInfo::queryDisplayInfo() {
+    if (Properties::isolatedProcess) {
+        return sDummyDisplay;
+    }
+
+    DisplayInfo displayInfo;
     sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
-    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo);
+    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &displayInfo);
     LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status);
+    return displayInfo;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index a0b2f06..297b266 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -47,12 +47,13 @@
         return di.w * di.h * in;
     }
 
+    static DisplayInfo queryDisplayInfo();
+
 private:
     DeviceInfo() {}
     ~DeviceInfo() {}
 
     void load();
-    void loadDisplayInfo();
 
     int mMaxTextureSize;
     DisplayInfo mDisplayInfo;
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 8110664..81a7980 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -38,16 +38,27 @@
 namespace uirenderer {
 
 struct Comparison {
+    JankType type;
+    std::function<int64_t(nsecs_t)> computeThreadshold;
     FrameInfoIndex start;
     FrameInfoIndex end;
 };
 
-static const Comparison COMPARISONS[] = {
-        {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
-        {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
-        {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
-        {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
-        {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
+static const std::array<Comparison, 4> COMPARISONS{
+        Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync,
+                   FrameInfoIndex::Vsync},
+
+        Comparison{JankType::kSlowUI,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.5 * frameInterval); },
+                   FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
+
+        Comparison{JankType::kSlowSync,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.2 * frameInterval); },
+                   FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
+
+        Comparison{JankType::kSlowRT,
+                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.75 * frameInterval); },
+                   FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
 };
 
 // If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -91,24 +102,10 @@
 
 void JankTracker::setFrameInterval(nsecs_t frameInterval) {
     mFrameInterval = frameInterval;
-    mThresholds[kMissedVsync] = 1;
-    /*
-     * Due to interpolation and sample rate differences between the touch
-     * panel and the display (example, 85hz touch panel driving a 60hz display)
-     * we call high latency 1.5 * frameinterval
-     *
-     * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
-     * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
-     * Thus this must always be larger than frameInterval, or it will fail
-     */
-    mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
 
-    // Note that these do not add up to 1. This is intentional. It's to deal
-    // with variance in values, and should be sort of an upper-bound on what
-    // is reasonable to expect.
-    mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
-    mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
-    mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+    for (auto& comparison : COMPARISONS) {
+        mThresholds[comparison.type] = comparison.computeThreadshold(frameInterval);
+    }
 }
 
 void JankTracker::finishFrame(const FrameInfo& frame) {
@@ -166,11 +163,11 @@
     nsecs_t lastFrameOffset = jitterNanos % mFrameInterval;
     mSwapDeadline = frame[FrameInfoIndex::FrameCompleted] - lastFrameOffset + mFrameInterval;
 
-    for (int i = 0; i < NUM_BUCKETS; i++) {
-        int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
-        if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
-            mData->reportJankType((JankType)i);
-            (*mGlobalData)->reportJankType((JankType)i);
+    for (auto& comparison : COMPARISONS) {
+        int64_t delta = frame.duration(comparison.start, comparison.end);
+        if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) {
+            mData->reportJankType(comparison.type);
+            (*mGlobalData)->reportJankType(comparison.type);
         }
     }
 
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e9adbdc..1602b4b 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -63,6 +63,7 @@
 
 bool Properties::runningInEmulator = false;
 bool Properties::debuggingEnabled = false;
+bool Properties::isolatedProcess = false;
 
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3f44c21..81a3657 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -269,6 +269,7 @@
     static bool runningInEmulator;
 
     ANDROID_API static bool debuggingEnabled;
+    ANDROID_API static bool isolatedProcess;
 
 private:
     static ProfileType sProfileType;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 28d0bc4..c529f87 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -26,8 +26,8 @@
 
 namespace android {
 
-AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage)
-        : mSkAnimatedImage(std::move(animatedImage)) {
+AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
+        : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
     mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
 }
 
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index f4e2ba7..a92b62d 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -45,7 +45,9 @@
  */
 class ANDROID_API AnimatedImageDrawable : public SkDrawable {
 public:
-    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage);
+    // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the
+    // Snapshots.
+    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
 
     /**
      * This updates the internal time and returns true if the animation needs
@@ -100,11 +102,17 @@
     Snapshot decodeNextFrame();
     Snapshot reset();
 
+    size_t byteSize() const {
+        return sizeof(this) + mBytesUsed;
+    }
+
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
+    const size_t mBytesUsed;
+
     bool mRunning = false;
     bool mStarting = false;
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d04bb2e..f341cf9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -276,6 +276,8 @@
                         const SkPath& path, float hOffset, float vOffset, const Paint& paint,
                         const Typeface* typeface);
 
+    static int GetApiLevel() { return sApiLevel; }
+
 protected:
     void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9db39d9..0cd1c15 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -342,7 +342,8 @@
     SkAutoCanvasRestore saver(canvas, true);
     canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
 
-    if (!opaque) {
+    // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
+    if (!opaque || wideColorGamut) {
         canvas->clear(SK_ColorTRANSPARENT);
     }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 820789d..f4d8051 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -355,7 +355,14 @@
             // Already drew for this vsync pulse, UI draw request missed
             // the deadline for RT animations
             info.out.canDrawThisFrame = false;
-        } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
+        }
+        /* This logic exists to try and recover from a display latch miss, which essentially
+         * results in the bufferqueue being double-buffered instead of triple-buffered.
+         * SurfaceFlinger itself now tries to handle & recover from this situation, so this
+         * logic should no longer be necessary. As it's occasionally triggering when
+         * undesired disable it.
+         * TODO: Remove this entirely if the results are solid.
+        else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
                    (latestVsync - mLastDropVsync) < 500_ms) {
             // It's been several frame intervals, assume the buffer queue is fine
             // or the last drop was too recent
@@ -367,6 +374,7 @@
                 mLastDropVsync = mRenderThread.timeLord().latestVsync();
             }
         }
+        */
     } else {
         info.out.canDrawThisFrame = true;
     }
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 8e0546b..6a2a025 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,6 +17,7 @@
 #include "RenderThread.h"
 
 #include "CanvasContext.h"
+#include "DeviceInfo.h"
 #include "EglManager.h"
 #include "OpenGLReadback.h"
 #include "RenderProxy.h"
@@ -29,10 +30,9 @@
 #include "renderstate/RenderState.h"
 #include "renderthread/OpenGLPipeline.h"
 #include "utils/FatVector.h"
+#include "utils/TimeUtils.h"
 
 #include <gui/DisplayEventReceiver.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
 #include <sys/resource.h>
 #include <utils/Condition.h>
 #include <utils/Log.h>
@@ -54,6 +54,58 @@
 
 static void (*gOnStartHook)() = nullptr;
 
+class DisplayEventReceiverWrapper : public VsyncSource {
+public:
+    DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver)
+            : mDisplayEventReceiver(std::move(receiver)) {}
+
+    virtual void requestNextVsync() override {
+        status_t status = mDisplayEventReceiver->requestNextVsync();
+        LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status);
+    }
+
+    virtual nsecs_t latestVsyncEvent() override {
+        DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+        nsecs_t latest = 0;
+        ssize_t n;
+        while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+            for (ssize_t i = 0; i < n; i++) {
+                const DisplayEventReceiver::Event& ev = buf[i];
+                switch (ev.header.type) {
+                    case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                        latest = ev.header.timestamp;
+                        break;
+                }
+            }
+        }
+        if (n < 0) {
+            ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+        }
+        return latest;
+    }
+
+private:
+    std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
+};
+
+class DummyVsyncSource : public VsyncSource {
+public:
+    DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
+
+    virtual void requestNextVsync() override {
+        mRenderThread->queue().postDelayed(16_ms, [this]() {
+            mRenderThread->drainDisplayEventQueue();
+        });
+    }
+
+    virtual nsecs_t latestVsyncEvent() override {
+        return systemTime(CLOCK_MONOTONIC);
+    }
+
+private:
+    RenderThread* mRenderThread;
+};
+
 bool RenderThread::hasInstance() {
     return gHasRenderThreadInstance;
 }
@@ -74,7 +126,7 @@
 
 RenderThread::RenderThread()
         : ThreadBase()
-        , mDisplayEventReceiver(nullptr)
+        , mVsyncSource(nullptr)
         , mVsyncRequested(false)
         , mFrameCallbackTaskPending(false)
         , mRenderState(nullptr)
@@ -89,23 +141,27 @@
 }
 
 void RenderThread::initializeDisplayEventReceiver() {
-    LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
-    mDisplayEventReceiver = new DisplayEventReceiver();
-    status_t status = mDisplayEventReceiver->initCheck();
-    LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                        "Initialization of DisplayEventReceiver "
-                        "failed with status: %d",
-                        status);
+    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?");
 
-    // Register the FD
-    mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
-                   RenderThread::displayEventReceiverCallback, this);
+    if (!Properties::isolatedProcess) {
+        auto receiver = std::make_unique<DisplayEventReceiver>();
+        status_t status = receiver->initCheck();
+        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+                "Initialization of DisplayEventReceiver "
+                        "failed with status: %d",
+                status);
+
+        // Register the FD
+        mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
+                RenderThread::displayEventReceiverCallback, this);
+        mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
+    } else {
+        mVsyncSource = new DummyVsyncSource(this);
+    }
 }
 
 void RenderThread::initThreadLocals() {
-    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
-    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo);
-    LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
+    mDisplayInfo = DeviceInfo::queryDisplayInfo();
     nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
     mTimeLord.setFrameInterval(frameIntervalNanos);
     initializeDisplayEventReceiver();
@@ -201,29 +257,9 @@
     return 1;  // keep the callback
 }
 
-static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    nsecs_t latest = 0;
-    ssize_t n;
-    while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        for (ssize_t i = 0; i < n; i++) {
-            const DisplayEventReceiver::Event& ev = buf[i];
-            switch (ev.header.type) {
-                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                    latest = ev.header.timestamp;
-                    break;
-            }
-        }
-    }
-    if (n < 0) {
-        ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
-    }
-    return latest;
-}
-
 void RenderThread::drainDisplayEventQueue() {
     ATRACE_CALL();
-    nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+    nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent();
     if (vsyncEvent > 0) {
         mVsyncRequested = false;
         if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {
@@ -256,8 +292,7 @@
 void RenderThread::requestVsync() {
     if (!mVsyncRequested) {
         mVsyncRequested = true;
-        status_t status = mDisplayEventReceiver->requestNextVsync();
-        LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status);
+        mVsyncSource->requestNextVsync();
     }
 }
 
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 3aa5487..689f518 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -39,7 +39,6 @@
 namespace android {
 
 class Bitmap;
-class DisplayEventReceiver;
 
 namespace uirenderer {
 
@@ -63,6 +62,14 @@
     ~IFrameCallback() {}
 };
 
+struct VsyncSource {
+    virtual void requestNextVsync() = 0;
+    virtual nsecs_t latestVsyncEvent() = 0;
+    virtual ~VsyncSource() {}
+};
+
+class DummyVsyncSource;
+
 class RenderThread : private ThreadBase {
     PREVENT_COPY_AND_ASSIGN(RenderThread);
 
@@ -110,6 +117,7 @@
 private:
     friend class DispatchFrameCallbacks;
     friend class RenderProxy;
+    friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
 
     RenderThread();
@@ -127,7 +135,7 @@
 
     DisplayInfo mDisplayInfo;
 
-    DisplayEventReceiver* mDisplayEventReceiver;
+    VsyncSource* mVsyncSource;
     bool mVsyncRequested;
     std::set<IFrameCallback*> mFrameCallbacks;
     // We defer the actual registration of these callbacks until
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 406255d..9e6d9a8 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -21,6 +21,7 @@
 #include "debug/GlesDriver.h"
 #include "debug/NullGlesDriver.h"
 #include "hwui/Typeface.h"
+#include "Properties.h"
 #include "tests/common/LeakChecker.h"
 #include "thread/TaskManager.h"
 
@@ -67,6 +68,7 @@
 
     // Replace the default GLES driver
     debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>());
+    Properties::isolatedProcess = true;
 
     // Run the tests
     testing::InitGoogleTest(&argc, argv);
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 412cc291..2152e1e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -83,6 +83,20 @@
      */
     public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
 
+    /**
+     * GNSS measurement tracking loop state
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "STATE_" }, value = {
+            STATE_CODE_LOCK, STATE_BIT_SYNC, STATE_SUBFRAME_SYNC,
+            STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
+            STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
+            STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
+            STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
     /** This GNSS measurement's tracking state is invalid or unknown. */
     public static final int STATE_UNKNOWN = 0;
     /** This GNSS measurement's tracking state has code lock. */
@@ -133,29 +147,68 @@
     private static final int STATE_ALL = 0x3fff;  // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
 
     /**
-     * The state of the 'Accumulated Delta Range' is invalid or unknown.
+     * GNSS measurement accumulated delta range state
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
+            ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
+            ADR_STATE_HALF_CYCLE_REPORTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AdrState {}
+
+    /**
+     * The state of the value {@link #getAccumulatedDeltaRangeMeters()} is invalid or unknown.
      */
     public static final int ADR_STATE_UNKNOWN = 0;
 
     /**
-     * The state of the 'Accumulated Delta Range' is valid.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} is valid.
      */
     public static final int ADR_STATE_VALID = (1<<0);
 
     /**
-     * The state of the 'Accumulated Delta Range' has detected a reset.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} has detected a reset.
      */
     public static final int ADR_STATE_RESET = (1<<1);
 
     /**
-     * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} has a cycle slip detected.
      */
     public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
 
     /**
-     * All the 'Accumulated Delta Range' flags.
+     * Reports whether the value {@link #getAccumulatedDeltaRangeMeters()} has resolved the half
+     * cycle ambiguity.
+     *
+     * <p> When this bit is set, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+     * carrier phase measurement plus an accumulated integer number of carrier full cycles.
+     *
+     * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+     * carrier phase measurement plus an accumulated integer number of carrier half cycles.
      */
-    private static final int ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+    public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
+
+    /**
+     * Reports whether the flag {@link #ADR_STATE_HALF_CYCLE_RESOLVED} has been reported by the
+     * GNSS hardware.
+     *
+     * <p> When this bit is set, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+     * can be low (centimeter level) whether or not the half cycle ambiguity is resolved.
+     *
+     * <p> When this bit is unset, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+     * is larger, to cover the potential error due to half cycle ambiguity being unresolved.
+     */
+    public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
+    /**
+     * All the 'Accumulated Delta Range' flags.
+     * @hide
+     */
+    @TestApi
+    public static final int ADR_STATE_ALL =
+            ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP |
+            ADR_STATE_HALF_CYCLE_RESOLVED | ADR_STATE_HALF_CYCLE_REPORTED;
 
     // End enumerations in sync with gps.h
 
@@ -278,6 +331,7 @@
      *
      * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
      */
+    @State
     public int getState() {
         return mState;
     }
@@ -287,7 +341,7 @@
      * @hide
      */
     @TestApi
-    public void setState(int value) {
+    public void setState(@State int value) {
         mState = value;
     }
 
@@ -557,6 +611,7 @@
      * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
      * cycle slip (indicating 'loss of lock').
      */
+    @AdrState
     public int getAccumulatedDeltaRangeState() {
         return mAccumulatedDeltaRangeState;
     }
@@ -566,7 +621,7 @@
      * @hide
      */
     @TestApi
-    public void setAccumulatedDeltaRangeState(int value) {
+    public void setAccumulatedDeltaRangeState(@AdrState int value) {
         mAccumulatedDeltaRangeState = value;
     }
 
@@ -589,7 +644,15 @@
         if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
             builder.append("CycleSlip|");
         }
-        int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+        if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_RESOLVED) ==
+                ADR_STATE_HALF_CYCLE_RESOLVED) {
+            builder.append("HalfCycleResolved|");
+        }
+        if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_REPORTED)
+                == ADR_STATE_HALF_CYCLE_REPORTED) {
+            builder.append("HalfCycleReported|");
+        }
+        int remainingStates = mAccumulatedDeltaRangeState & ~ADR_STATE_ALL;
         if (remainingStates > 0) {
             builder.append("Other(");
             builder.append(Integer.toBinaryString(remainingStates));
@@ -674,7 +737,7 @@
      * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
      * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
      *
-     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+     * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
      * measurement objects will be reported for this same satellite, in one of the measurement
      * objects, all the values related to L1 will be filled, and in the other all of the values
      * related to L5 will be filled.
@@ -709,7 +772,10 @@
 
     /**
      * Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierCycles() {
         return isFlagSet(HAS_CARRIER_CYCLES);
     }
@@ -720,16 +786,24 @@
      * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
      *
      * <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
      */
+    @Deprecated
     public long getCarrierCycles() {
         return mCarrierCycles;
     }
 
     /**
      * Sets the number of full carrier cycles between the satellite and the receiver.
+     *
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierCycles(long value) {
         setFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = value;
@@ -737,9 +811,13 @@
 
     /**
      * Resets the number of full carrier cycles between the satellite and the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierCycles() {
         resetFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = Long.MIN_VALUE;
@@ -747,7 +825,10 @@
 
     /**
      * Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierPhase() {
         return isFlagSet(HAS_CARRIER_PHASE);
     }
@@ -764,16 +845,24 @@
      * <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
      *
      * <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
      */
+    @Deprecated
     public double getCarrierPhase() {
         return mCarrierPhase;
     }
 
     /**
      * Sets the RF phase detected by the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierPhase(double value) {
         setFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = value;
@@ -781,9 +870,14 @@
 
     /**
      * Resets the RF phase detected by the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierPhase() {
         resetFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = Double.NaN;
@@ -792,7 +886,10 @@
     /**
      * Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
      * otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierPhaseUncertainty() {
         return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
     }
@@ -803,16 +900,24 @@
      * <p>The uncertainty is represented as an absolute (single sided) value.
      *
      * <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeUncertaintyMeters()} instead.
      */
+    @Deprecated
     public double getCarrierPhaseUncertainty() {
         return mCarrierPhaseUncertainty;
     }
 
     /**
      * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierPhaseUncertainty(double value) {
         setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = value;
@@ -820,9 +925,14 @@
 
     /**
      * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierPhaseUncertainty() {
         resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = Double.NaN;
@@ -859,7 +969,7 @@
             case MULTIPATH_INDICATOR_NOT_DETECTED:
                 return "NotDetected";
             default:
-                return "<Invalid:" + mMultipathIndicator + ">";
+                return "<Invalid: " + mMultipathIndicator + ">";
         }
     }
 
@@ -916,11 +1026,12 @@
      * number. Hence in cases of strong jamming, in the band of this signal, this value will go more
      * negative.
      *
-     * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+     * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
      * components) may also affect the typical output of of this value on any given hardware design
      * in an open sky test - the important aspect of this output is that changes in this value are
      * indicative of changes on input signal power in the frequency band for this measurement.
-     * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+     *
+     * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
      */
     public double getAutomaticGainControlLevelDb() {
         return mAutomaticGainControlLevelInDb;
diff --git a/location/java/android/location/IFusedProvider.aidl b/location/java/android/location/IFusedProvider.aidl
deleted file mode 100644
index e86ad1a..0000000
--- a/location/java/android/location/IFusedProvider.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.hardware.location.IFusedLocationHardware;
-
-/**
- * Interface definition for Location providers that require FLP services.
- * @hide
- */
-oneway interface IFusedProvider {
-    /**
-     * Provides access to a FusedLocationHardware instance needed for the provider to work.
-     *
-     * @param instance      The FusedLocationHardware available for the provider to use.
-     */
-    void onFusedLocationHardwareChange(in IFusedLocationHardware instance);
-}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 2dd8c36..a523958 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -23,6 +23,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
@@ -42,12 +43,14 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.internal.location.ProviderProperties;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This class provides access to the system location services.  These
@@ -234,12 +237,6 @@
         "android.location.HIGH_POWER_REQUEST_CHANGE";
 
     /**
-     * The value returned by {@link LocationManager#getGnssHardwareModelName()} when the hardware
-     * does not support providing the actual value.
-     */
-    public static final String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
-
-    /**
      * Broadcast intent action for Settings app to inject a footer at the bottom of location
      * settings.
      *
@@ -1252,12 +1249,40 @@
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        for (String provider : getAllProviders()) {
+        final List<String> allProvidersList = getAllProviders();
+        // Update all providers on device plus gps and network provider when disabling location.
+        Set<String> allProvidersSet = new ArraySet<>(allProvidersList.size() + 2);
+        allProvidersSet.addAll(allProvidersList);
+        // When disabling location, disable gps and network provider that could have been enabled by
+        // location mode api.
+        if (enabled == false) {
+            allProvidersSet.add(GPS_PROVIDER);
+            allProvidersSet.add(NETWORK_PROVIDER);
+        }
+        if (allProvidersSet.isEmpty()) {
+            return;
+        }
+        // to ensure thread safety, we write the provider name with a '+' or '-'
+        // and let the SettingsProvider handle it rather than reading and modifying
+        // the list of enabled providers.
+        final String prefix = enabled ? "+" : "-";
+        StringBuilder locationProvidersAllowed = new StringBuilder();
+        for (String provider : allProvidersSet) {
+            checkProvider(provider);
             if (provider.equals(PASSIVE_PROVIDER)) {
                 continue;
             }
-            setProviderEnabledForUser(provider, enabled, userHandle);
+            locationProvidersAllowed.append(prefix);
+            locationProvidersAllowed.append(provider);
+            locationProvidersAllowed.append(",");
         }
+        // Remove the trailing comma
+        locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+        Settings.Secure.putStringForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                locationProvidersAllowed.toString(),
+                userHandle.getIdentifier());
     }
 
     /**
@@ -2176,7 +2201,9 @@
     /**
      * Returns the model year of the GNSS hardware and software build.
      *
-     * May return 0 if the model year is less than 2016.
+     * <p> More details, such as build date, may be available in {@link #getGnssHardwareModelName()}.
+     *
+     * <p> May return 0 if the model year is less than 2016.
      */
     public int getGnssYearOfHardware() {
         try {
@@ -2190,10 +2217,12 @@
      * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
      * driver.
      *
-     * Will return {@link LocationManager#GNSS_HARDWARE_MODEL_NAME_UNKNOWN} when the GNSS hardware
-     * abstraction layer does not support providing this value.
+     * <p> No device-specific serial number or ID is returned from this API.
+     *
+     * <p> Will return null when the GNSS hardware abstraction layer does not support providing
+     * this value.
      */
-    @NonNull
+    @Nullable
     public String getGnssHardwareModelName() {
         try {
             return mService.getGnssHardwareModelName();
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 6abba95..96a0817 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -339,7 +339,8 @@
      * substantially restrict power.
      *
      * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
-     * signal searches for more than one second per interval {@link #mInterval}
+     * signal searches for more than one second per interval (specified by
+     * {@link #setInterval(long)}).
      *
      * @param enabled Enable or disable low power mode
      * @return the same object, so that setters can be chained
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
index 8424601..6642134 100644
--- a/location/lib/Android.mk
+++ b/location/lib/Android.mk
@@ -42,3 +42,25 @@
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 
 include $(BUILD_PREBUILT)
+
+# ==== Stub library  ===========================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.location.provider-stubs-gen
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := $(call all-java-files-under,java)
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.location.provider.stubs_intermediates/src
+LOCAL_DROIDDOC_OPTIONS:= \
+    -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
+    -stubpackages com.android.location.provider \
+    -nodocs
+LOCAL_UNINSTALLABLE_MODULE := true
+include $(BUILD_DROIDDOC)
+com_android_nfc_extras_gen_stamp := $(full_target)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.location.provider.stubs
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
+LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_nfc_extras_gen_stamp)
+com_android_nfc_extras_gen_stamp :=
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
index c7dfc88..843dd67 100644
--- a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
+++ b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
@@ -23,6 +23,7 @@
 
 /**
  * A class representing an event for Activity changes.
+ * @hide
  */
 public class ActivityChangedEvent {
     private final List<ActivityRecognitionEvent> mActivityRecognitionEvents;
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
index a39cff2..e54dea4 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
@@ -18,6 +18,7 @@
 
 /**
  * A class that represents an Activity Recognition Event.
+ * @hide
  */
 public class ActivityRecognitionEvent {
     private final String mActivity;
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index bc2dae1..0eff7d3 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -28,6 +28,7 @@
 
 /**
  * A class that exposes {@link IActivityRecognitionHardware} functionality to unbundled services.
+ * @hide
  */
 public final class ActivityRecognitionProvider {
     private final IActivityRecognitionHardware mService;
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
index 0b878d7..326d901 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
@@ -27,6 +27,7 @@
 
 /**
  * A client class for interaction with an Activity-Recognition provider.
+ * @hide
  */
 public abstract class ActivityRecognitionProviderClient {
     private static final String TAG = "ArProviderClient";
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
index 7139025..42f77b4 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
@@ -30,6 +30,7 @@
  * A watcher class for Activity-Recognition instances.
  *
  * @deprecated use {@link ActivityRecognitionProviderClient} instead.
+ * @hide
  */
 @Deprecated
 public class ActivityRecognitionProviderWatcher {
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
deleted file mode 100644
index eb3b2f4..0000000
--- a/location/lib/java/com/android/location/provider/FusedLocationHardware.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (C) 2013 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.location.provider;
-
-import android.hardware.location.IFusedLocationHardware;
-import android.hardware.location.IFusedLocationHardwareSink;
-
-import android.location.Location;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Class that exposes IFusedLocationHardware functionality to unbundled services.
- */
-public final class FusedLocationHardware {
-    private static final String TAG = "FusedLocationHardware";
-
-    private IFusedLocationHardware mLocationHardware;
-
-    // the list uses a copy-on-write pattern to update its contents
-    HashMap<FusedLocationHardwareSink, DispatcherHandler> mSinkList =
-            new HashMap<FusedLocationHardwareSink, DispatcherHandler>();
-
-    private IFusedLocationHardwareSink mInternalSink = new IFusedLocationHardwareSink.Stub() {
-        @Override
-        public void onLocationAvailable(Location[] locations) {
-            dispatchLocations(locations);
-        }
-
-        @Override
-        public void onDiagnosticDataAvailable(String data) {
-            dispatchDiagnosticData(data);
-        }
-
-        @Override
-        public void onCapabilities(int capabilities) {
-            dispatchCapabilities(capabilities);
-        }
-
-        @Override
-        public void onStatusChanged(int status) {
-            dispatchStatus(status);
-        }
-    };
-
-    /**
-     * @hide
-     */
-    public FusedLocationHardware(IFusedLocationHardware locationHardware) {
-        mLocationHardware = locationHardware;
-    }
-
-    /*
-     * Methods to provide a Facade for IFusedLocationHardware
-     */
-    public void registerSink(FusedLocationHardwareSink sink, Looper looper) {
-        if(sink == null || looper == null) {
-            throw new IllegalArgumentException("Parameter sink and looper cannot be null.");
-        }
-
-        boolean registerSink;
-        synchronized (mSinkList) {
-            // register only on first insertion
-            registerSink = mSinkList.size() == 0;
-            // guarantee uniqueness
-            if(mSinkList.containsKey(sink)) {
-                return;
-            }
-
-            HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList =
-                    new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList);
-            newSinkList.put(sink, new DispatcherHandler(looper));
-            mSinkList = newSinkList;
-        }
-
-        if(registerSink) {
-            try {
-                mLocationHardware.registerSink(mInternalSink);
-            } catch(RemoteException e) {
-                Log.e(TAG, "RemoteException at registerSink");
-            }
-        }
-    }
-
-    public void unregisterSink(FusedLocationHardwareSink sink) {
-        if(sink == null) {
-            throw new IllegalArgumentException("Parameter sink cannot be null.");
-        }
-
-        boolean unregisterSink;
-        synchronized(mSinkList) {
-            if(!mSinkList.containsKey(sink)) {
-                //done
-                return;
-            }
-
-            HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList =
-                    new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList);
-            newSinkList.remove(sink);
-            //unregister after the last sink
-            unregisterSink = newSinkList.size() == 0;
-
-            mSinkList = newSinkList;
-        }
-
-        if(unregisterSink) {
-            try {
-                mLocationHardware.unregisterSink(mInternalSink);
-            } catch(RemoteException e) {
-                Log.e(TAG, "RemoteException at unregisterSink");
-            }
-        }
-    }
-
-    public int getSupportedBatchSize() {
-        try {
-            return mLocationHardware.getSupportedBatchSize();
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at getSupportedBatchSize");
-            return 0;
-        }
-    }
-
-    public void startBatching(int id, GmsFusedBatchOptions batchOptions) {
-        try {
-            mLocationHardware.startBatching(id, batchOptions.getParcelableOptions());
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at startBatching");
-        }
-    }
-
-    public void stopBatching(int id) {
-        try {
-            mLocationHardware.stopBatching(id);
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at stopBatching");
-        }
-    }
-
-    public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) {
-        try {
-            mLocationHardware.updateBatchingOptions(id, batchOptions.getParcelableOptions());
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at updateBatchingOptions");
-        }
-    }
-
-    public void requestBatchOfLocations(int batchSizeRequest) {
-        try {
-            mLocationHardware.requestBatchOfLocations(batchSizeRequest);
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at requestBatchOfLocations");
-        }
-    }
-
-    public void flushBatchedLocations() {
-        try {
-            mLocationHardware.flushBatchedLocations();
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at flushBatchedLocations");
-        }
-    }
-
-    public boolean supportsDiagnosticDataInjection() {
-        try {
-            return mLocationHardware.supportsDiagnosticDataInjection();
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at supportsDiagnisticDataInjection");
-            return false;
-        }
-    }
-
-    public void injectDiagnosticData(String data) {
-        try {
-            mLocationHardware.injectDiagnosticData(data);
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at injectDiagnosticData");
-        }
-    }
-
-    public boolean supportsDeviceContextInjection() {
-        try {
-            return mLocationHardware.supportsDeviceContextInjection();
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at supportsDeviceContextInjection");
-            return false;
-        }
-    }
-
-    public void injectDeviceContext(int deviceEnabledContext) {
-        try {
-            mLocationHardware.injectDeviceContext(deviceEnabledContext);
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at injectDeviceContext");
-        }
-    }
-
-
-    /**
-     * Returns the version of the FLP HAL.
-     *
-     * <p>Version 1 is the initial release.
-     * <p>Version 2 adds the ability to use {@link #flushBatchedLocations},
-     * {@link FusedLocationHardwareSink#onCapabilities}, and
-     * {@link FusedLocationHardwareSink#onStatusChanged}.
-     *
-     * <p>This method is only available on API 23 or later.  Older APIs have version 1.
-     */
-    public int getVersion() {
-        try {
-            return mLocationHardware.getVersion();
-        } catch(RemoteException e) {
-            Log.e(TAG, "RemoteException at getVersion");
-        }
-        return 1;
-    }
-
-    /*
-     * Helper methods and classes
-     */
-    private class DispatcherHandler extends Handler {
-        public static final int DISPATCH_LOCATION = 1;
-        public static final int DISPATCH_DIAGNOSTIC_DATA = 2;
-        public static final int DISPATCH_CAPABILITIES = 3;
-        public static final int DISPATCH_STATUS = 4;
-
-        public DispatcherHandler(Looper looper) {
-            super(looper, null /*callback*/ , true /*async*/);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            MessageCommand command = (MessageCommand) message.obj;
-            switch(message.what) {
-                case DISPATCH_LOCATION:
-                    command.dispatchLocation();
-                    break;
-                case DISPATCH_DIAGNOSTIC_DATA:
-                    command.dispatchDiagnosticData();
-                    break;
-                case DISPATCH_CAPABILITIES:
-                    command.dispatchCapabilities();
-                    break;
-                case DISPATCH_STATUS:
-                    command.dispatchStatus();
-                    break;
-                default:
-                    Log.e(TAG, "Invalid dispatch message");
-                    break;
-            }
-        }
-    }
-
-    private class MessageCommand {
-        private final FusedLocationHardwareSink mSink;
-        private final Location[] mLocations;
-        private final String mData;
-        private final int mCapabilities;
-        private final int mStatus;
-
-        public MessageCommand(
-                FusedLocationHardwareSink sink,
-                Location[] locations,
-                String data,
-                int capabilities,
-                int status) {
-            mSink = sink;
-            mLocations = locations;
-            mData = data;
-            mCapabilities = capabilities;
-            mStatus = status;
-        }
-
-        public void dispatchLocation() {
-            mSink.onLocationAvailable(mLocations);
-        }
-
-        public void dispatchDiagnosticData() {
-            mSink.onDiagnosticDataAvailable(mData);
-        }
-
-        public void dispatchCapabilities() {
-            mSink.onCapabilities(mCapabilities);
-        }
-
-        public void dispatchStatus() {
-            mSink.onStatusChanged(mStatus);
-        }
-    }
-
-    private void dispatchLocations(Location[] locations) {
-        HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks;
-        synchronized (mSinkList) {
-            sinks = mSinkList;
-        }
-
-        for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) {
-            Message message = Message.obtain(
-                    entry.getValue(),
-                    DispatcherHandler.DISPATCH_LOCATION,
-                    new MessageCommand(entry.getKey(), locations, null /*data*/, 0, 0));
-            message.sendToTarget();
-        }
-    }
-
-    private void dispatchDiagnosticData(String data) {
-        HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks;
-        synchronized(mSinkList) {
-            sinks = mSinkList;
-        }
-
-        for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) {
-            Message message = Message.obtain(
-                    entry.getValue(),
-                    DispatcherHandler.DISPATCH_DIAGNOSTIC_DATA,
-                    new MessageCommand(entry.getKey(), null /*locations*/, data, 0, 0));
-            message.sendToTarget();
-        }
-    }
-
-    private void dispatchCapabilities(int capabilities) {
-        HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks;
-        synchronized(mSinkList) {
-            sinks = mSinkList;
-        }
-
-        for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) {
-            Message message = Message.obtain(
-                    entry.getValue(),
-                    DispatcherHandler.DISPATCH_CAPABILITIES,
-                    new MessageCommand(entry.getKey(), null /*locations*/, null, capabilities, 0));
-            message.sendToTarget();
-        }
-    }
-
-    private void dispatchStatus(int status) {
-        HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks;
-        synchronized(mSinkList) {
-            sinks = mSinkList;
-        }
-
-        for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) {
-            Message message = Message.obtain(
-                    entry.getValue(),
-                    DispatcherHandler.DISPATCH_STATUS,
-                    new MessageCommand(entry.getKey(), null /*locations*/, null, 0, status));
-            message.sendToTarget();
-        }
-    }
-}
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
deleted file mode 100644
index 01d37ac..0000000
--- a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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.location.provider;
-
-import android.location.Location;
-
-/**
- * Base class for sinks to interact with FusedLocationHardware.
- *
- * <p>Default implementations allow new methods to be added without crashing
- * clients compiled against an old library version.
- */
-public class FusedLocationHardwareSink {
-    /**
-     * Called when one or more locations are available from the FLP
-     * HAL.
-     */
-    public void onLocationAvailable(Location[] locations) {
-        // default do nothing
-    }
-
-    /**
-     * Called when diagnostic data is available from the FLP HAL.
-     */
-    public void onDiagnosticDataAvailable(String data) {
-        // default do nothing
-    }
-
-    /**
-     * Called when capabilities are available from the FLP HAL.
-     * Should be called once right after initialization.
-     *
-     * @param capabilities A bitmask of capabilities defined in
-     *                     fused_location.h.
-     */
-    public void onCapabilities(int capabilities) {
-        // default do nothing
-    }
-
-    /**
-     * Called when the status changes in the underlying FLP HAL
-     * implementation (the ability to compute location).  This
-     * callback will only be made on version 2 or later
-     * (see {@link FusedLocationHardware#getVersion()}).
-     *
-     * @param status One of FLP_STATUS_LOCATION_AVAILABLE or
-     *               FLP_STATUS_LOCATION_UNAVAILABLE as defined in
-     *               fused_location.h.
-     */
-    public void onStatusChanged(int status) {
-        // default do nothing
-    }
-}
\ No newline at end of file
diff --git a/location/lib/java/com/android/location/provider/FusedProvider.java b/location/lib/java/com/android/location/provider/FusedProvider.java
index c966ade..78a593b 100644
--- a/location/lib/java/com/android/location/provider/FusedProvider.java
+++ b/location/lib/java/com/android/location/provider/FusedProvider.java
@@ -16,8 +16,6 @@
 
 package com.android.location.provider;
 
-import android.hardware.location.IFusedLocationHardware;
-import android.location.IFusedProvider;
 import android.os.IBinder;
 
 /**
@@ -26,17 +24,12 @@
  * <p>Fused providers can be implemented as services and return the result of
  * {@link com.android.location.provider.FusedProvider#getBinder()} in its getBinder() method.
  *
- * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
- * API stable. See README.txt in the root of this package for more information.
+ * @deprecated This class should no longer be used. The location service does not uses this.
+ * This class exist here just to prevent existing apps having reference to this class from
+ * breaking.
  */
+@Deprecated
 public abstract class FusedProvider {
-    private IFusedProvider.Stub mProvider = new IFusedProvider.Stub() {
-        @Override
-        public void onFusedLocationHardwareChange(IFusedLocationHardware instance) {
-            setFusedLocationHardware(new FusedLocationHardware(instance));
-        }
-    };
-
     /**
      * Gets the Binder associated with the provider.
      * This is intended to be used for the onBind() method of a service that implements a fused
@@ -45,13 +38,6 @@
      * @return The IBinder instance associated with the provider.
      */
     public IBinder getBinder() {
-        return mProvider;
+        return null;
     }
-
-    /**
-     * Sets the FusedLocationHardware instance in the provider..
-     * @param value     The instance to set. This can be null in cases where the service connection
-     *                  is disconnected.
-     */
-    public abstract void setFusedLocationHardware(FusedLocationHardware value);
 }
diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
index d7a34af..f7f3d82 100644
--- a/location/lib/java/com/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -33,6 +33,7 @@
  * <p>IMPORTANT: This class is effectively a public API for unbundled
  * applications, and must remain API stable. See README.txt in the root
  * of this package for more information.
+ * @hide
  */
 public abstract class GeocodeProvider {
 
diff --git a/location/lib/java/com/android/location/provider/GeofenceProvider.java b/location/lib/java/com/android/location/provider/GeofenceProvider.java
index fafaa84..43690ab 100644
--- a/location/lib/java/com/android/location/provider/GeofenceProvider.java
+++ b/location/lib/java/com/android/location/provider/GeofenceProvider.java
@@ -31,6 +31,7 @@
  * <p>IMPORTANT: This class is effectively a public API for unbundled
  * applications, and must remain API stable. See README.txt in the root
  * of this package for more information.
+ * @hide
  */
 public abstract class GeofenceProvider {
 
diff --git a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
deleted file mode 100644
index 29818ec..0000000
--- a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2013 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.location.provider;
-
-import android.location.FusedBatchOptions;
-
-/**
- * Class that exposes FusedBatchOptions to the GmsCore .
- */
-public class GmsFusedBatchOptions {
-    private FusedBatchOptions mOptions = new FusedBatchOptions();
-
-    /*
-     * Methods that provide a facade for properties in FusedBatchOptions.
-     */
-    public void setMaxPowerAllocationInMW(double value) {
-        mOptions.setMaxPowerAllocationInMW(value);
-    }
-
-    public double getMaxPowerAllocationInMW() {
-        return mOptions.getMaxPowerAllocationInMW();
-    }
-
-    public void setPeriodInNS(long value) {
-        mOptions.setPeriodInNS(value);
-    }
-
-    public long getPeriodInNS() {
-        return mOptions.getPeriodInNS();
-    }
-
-    public void setSmallestDisplacementMeters(float value) {
-        mOptions.setSmallestDisplacementMeters(value);
-    }
-
-    public float getSmallestDisplacementMeters() {
-        return mOptions.getSmallestDisplacementMeters();
-    }
-
-    public void setSourceToUse(int source) {
-        mOptions.setSourceToUse(source);
-    }
-
-    public void resetSourceToUse(int source) {
-        mOptions.resetSourceToUse(source);
-    }
-
-    public boolean isSourceToUseSet(int source) {
-        return mOptions.isSourceToUseSet(source);
-    }
-
-    public int getSourcesToUse() {
-        return mOptions.getSourcesToUse();
-    }
-
-    public void setFlag(int flag) {
-        mOptions.setFlag(flag);
-    }
-
-    public void resetFlag(int flag) {
-        mOptions.resetFlag(flag);
-    }
-
-    public boolean isFlagSet(int flag) {
-        return mOptions.isFlagSet(flag);
-    }
-
-    public int getFlags() {
-        return mOptions.getFlags();
-    }
-
-    /**
-     * Definition of enum flag sets needed by this class.
-     * Such values need to be kept in sync with the ones in fused_location.h
-     */
-
-    public static final class SourceTechnologies {
-        public static int GNSS = 1<<0;
-        public static int WIFI = 1<<1;
-        public static int SENSORS = 1<<2;
-        public static int CELL = 1<<3;
-        public static int BLUETOOTH = 1<<4;
-    }
-
-    public static final class BatchFlags {
-        public static int WAKEUP_ON_FIFO_FULL = 1<<0;
-        public static int CALLBACK_ON_LOCATION_FIX = 1<<1;
-    }
-
-    /*
-     * Method definitions for internal use.
-     */
-
-    /*
-     * @hide
-     */
-    public FusedBatchOptions getParcelableOptions() {
-        return mOptions;
-    }
-}
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d717f40..30655f5 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -56,6 +56,7 @@
 public abstract class LocationProviderBase {
     private final String TAG;
 
+    /** @hide */
     protected final ILocationManager mLocationManager;
     private final ProviderProperties mProperties;
     private final IBinder mBinder;
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index 9ee4df21..b1a1bda 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -41,6 +41,7 @@
         mProperties = properties;
     }
 
+    /** @hide */
     public ProviderProperties getProviderProperties() {
         return mProperties;
     }
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index ad3d1df..6a8e618 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -33,6 +33,7 @@
 public final class ProviderRequestUnbundled {
     private final ProviderRequest mRequest;
 
+    /** @hide */
     public ProviderRequestUnbundled(ProviderRequest request) {
         mRequest = request;
     }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1536bb6..e408a11 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1352,6 +1352,7 @@
     //====================================================================
     // Offload query
     /**
+     * @hide
      * 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.
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 0a1de33..98c2d7f 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -42,6 +42,8 @@
 
     public abstract void setRingerModeInternal(int ringerMode, String caller);
 
+    public abstract void silenceRingerModeInternal(String caller);
+
     public abstract void updateRingerModeAffectedStreamsInternal();
 
     public abstract void setAccessibilityServiceUids(IntArray uids);
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 4652c18..e39cb7d 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -17,6 +17,9 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -88,10 +91,14 @@
      */
     public static final int MASTERED_FOR_HEADPHONE          = 4;
 
-    AudioPresentation(int presentationId,
+    /**
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public AudioPresentation(int presentationId,
                         int programId,
-                        Map<String, String> labels,
-                        String language,
+                        @NonNull Map<String, String> labels,
+                        @NonNull String language,
                         @MasteringIndicationType int masteringIndication,
                         boolean audioDescriptionAvailable,
                         boolean spokenSubtitlesAvailable,
@@ -112,6 +119,7 @@
      * decoder. Presentation id is typically sequential, but does not have to be.
      * @hide
      */
+    @VisibleForTesting
     public int getPresentationId() {
         return mPresentationId;
     }
@@ -121,13 +129,14 @@
      * Program id can be used to further uniquely identify the presentation to a decoder.
      * @hide
      */
+    @VisibleForTesting
     public int getProgramId() {
         return mProgramId;
     }
 
     /**
      * @return a map of available text labels for this presentation. Each label is indexed by its
-     * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B
+     * locale corresponding to the language code as specified by ISO 639-2. Either ISO 639-2/B
      * or ISO 639-2/T could be used.
      */
     public Map<Locale, String> getLabels() {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3885f90..acab8bb 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -747,7 +747,8 @@
     public static final int FOR_SYSTEM = 4;
     public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
     public static final int FOR_ENCODED_SURROUND = 6;
-    private static final int NUM_FORCE_USE = 7;
+    public static final int FOR_VIBRATE_RINGING = 7;
+    private static final int NUM_FORCE_USE = 8;
 
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 2d5fad5..87b5d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -896,6 +896,7 @@
         }
 
         /**
+         * @hide
          * 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
@@ -2012,9 +2013,10 @@
      * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
      * If a multi-stream decoder (MSD) is not present, or the format does not support
      * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+     * {@link #ERROR} is returned in case of any other error.
      * @param presentation see {@link AudioPresentation}. In particular, id should be set.
-     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
-     *    {@link #ERROR_INVALID_OPERATION}
+     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR},
+     *    {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
      * @throws IllegalArgumentException if the audio presentation is null.
      * @throws IllegalStateException if track is not initialized.
      */
@@ -2978,6 +2980,7 @@
     }
 
     /**
+     * @hide
      * 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.
@@ -3011,6 +3014,7 @@
     private final Object mStreamEventCbLock = new Object();
 
     /**
+     * @hide
      * 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 event notifications
@@ -3030,6 +3034,7 @@
     }
 
     /**
+     * @hide
      * Unregisters the callback for notification of stream events, previously set
      * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
      */
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 6d58a94..a53fa11 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -40,6 +40,7 @@
 import java.util.Map;
 
 /**
+ * @hide
  * Structure for data source descriptor.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 6dd4f69..37c5785 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -186,6 +186,13 @@
     public abstract long getTimestamp();
 
     /**
+     * Get the transformation associated with this frame.
+     * @return The window transformation that needs to be applied for this frame.
+     * @hide
+     */
+    public abstract int getTransform();
+
+    /**
      * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
      * intended for GPU and/or hardware access.
      * <p>
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index fb0de5c..56edace 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -876,6 +876,12 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+            return mTransform;
+        }
+
+        @Override
         public HardwareBuffer getHardwareBuffer() {
             throwISEIfImageIsInvalid();
             return nativeGetHardwareBuffer();
@@ -1013,6 +1019,11 @@
          */
         private long mTimestamp;
 
+        /**
+         * This field is set by native code during nativeImageSetup().
+         */
+        private int mTransform;
+
         private SurfacePlane[] mPlanes;
         private int mFormat = ImageFormat.UNKNOWN;
         // If this image is detached from the ImageReader.
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 2b7309f..8ee27ae 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -371,7 +371,7 @@
 
         Rect crop = image.getCropRect();
         nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
-                crop.right, crop.bottom);
+                crop.right, crop.bottom, image.getTransform());
 
         /**
          * Only remove and cleanup the Images that are owned by this
@@ -557,7 +557,8 @@
         // buffer caused leak.
         Rect crop = image.getCropRect();
         nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
-                image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom);
+                image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
+                image.getTransform());
     }
 
     /**
@@ -674,6 +675,8 @@
         private final long DEFAULT_TIMESTAMP = Long.MIN_VALUE;
         private long mTimestamp = DEFAULT_TIMESTAMP;
 
+        private int mTransform = 0; //Default no transform
+
         public WriterSurfaceImage(ImageWriter writer) {
             mOwner = writer;
         }
@@ -711,6 +714,13 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+
+            return mTransform;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
 
@@ -856,11 +866,11 @@
     private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi);
 
     private synchronized native void nativeQueueInputImage(long nativeCtx, Image image,
-            long timestampNs, int left, int top, int right, int bottom);
+            long timestampNs, int left, int top, int right, int bottom, int transform);
 
     private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
             long imageNativeBuffer, int imageFormat, long timestampNs, int left,
-            int top, int right, int bottom);
+            int top, int right, int bottom, int transform);
 
     private synchronized native void cancelImage(long nativeCtx, Image image);
 
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/Media2DataSource.java
index 8ee4a70..08df632 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/Media2DataSource.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 
 /**
+ * @hide
  * For supplying media data to the framework. Implement this if your app has
  * special requirements for the way media data is obtained.
  *
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index cf33958..452371a 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -30,6 +30,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Browses media content offered by a {@link MediaLibraryService2}.
  */
 public class MediaBrowser2 extends MediaController2 {
@@ -142,8 +143,8 @@
     @Override
     MediaBrowser2Provider createProvider(Context context, SessionToken2 token,
             Executor executor, ControllerCallback callback) {
-        return ApiLoader.getProvider(context)
-                .createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback);
+        return ApiLoader.getProvider().createMediaBrowser2(
+                context, this, token, executor, (BrowserCallback) callback);
     }
 
     /**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8a742b7..e3fba0c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2359,17 +2359,61 @@
     public static final int CRYPTO_MODE_AES_CBC     = 2;
 
     /**
-     * Metadata describing the structure of a (at least partially) encrypted
-     * input sample.
-     * A buffer's data is considered to be partitioned into "subSamples",
-     * each subSample starts with a (potentially empty) run of plain,
-     * unencrypted bytes followed by a (also potentially empty) run of
-     * encrypted bytes. If pattern encryption applies, each of the latter runs
-     * is encrypted only partly, according to a repeating pattern of "encrypt"
-     * and "skip" blocks. numBytesOfClearData can be null to indicate that all
-     * data is encrypted. This information encapsulates per-sample metadata as
-     * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
-     * media file format files".
+     * Metadata describing the structure of an encrypted input sample.
+     * <p>
+     * A buffer's data is considered to be partitioned into "subSamples". Each subSample starts with
+     * a run of plain, unencrypted bytes followed by a run of encrypted bytes. Either of these runs
+     * may be empty. If pattern encryption applies, each of the encrypted runs is encrypted only
+     * partly, according to a repeating pattern of "encrypt" and "skip" blocks.
+     * {@link #numBytesOfClearData} can be null to indicate that all data is encrypted, and
+     * {@link #numBytesOfEncryptedData} can be null to indicate that all data is clear. At least one
+     * of {@link #numBytesOfClearData} and {@link #numBytesOfEncryptedData} must be non-null.
+     * <p>
+     * This information encapsulates per-sample metadata as outlined in ISO/IEC FDIS 23001-7:2016
+     * "Common encryption in ISO base media file format files".
+     * <p>
+     * <h3>ISO-CENC Schemes</h3>
+     * ISO/IEC FDIS 23001-7:2016 defines four possible schemes by which media may be encrypted,
+     * corresponding to each possible combination of an AES mode with the presence or absence of
+     * patterned encryption.
+     *
+     * <table style="width: 0%">
+     *   <thead>
+     *     <tr>
+     *       <th>&nbsp;</th>
+     *       <th>AES-CTR</th>
+     *       <th>AES-CBC</th>
+     *     </tr>
+     *   </thead>
+     *   <tbody>
+     *     <tr>
+     *       <th>Without Patterns</th>
+     *       <td>cenc</td>
+     *       <td>cbc1</td>
+     *     </tr><tr>
+     *       <th>With Patterns</th>
+     *       <td>cens</td>
+     *       <td>cbcs</td>
+     *     </tr>
+     *   </tbody>
+     * </table>
+     *
+     * For {@code CryptoInfo}, the scheme is selected implicitly by the combination of the
+     * {@link #mode} field and the value set with {@link #setPattern}. For the pattern, setting the
+     * pattern to all zeroes (that is, both {@code blocksToEncrypt} and {@code blocksToSkip} are
+     * zero) is interpreted as turning patterns off completely. A scheme that does not use patterns
+     * will be selected, either cenc or cbc1. Setting the pattern to any nonzero value will choose
+     * one of the pattern-supporting schemes, cens or cbcs. The default pattern if
+     * {@link #setPattern} is never called is all zeroes.
+     * <p>
+     * <h4>HLS SAMPLE-AES Audio</h4>
+     * HLS SAMPLE-AES audio is encrypted in a manner compatible with the cbcs scheme, except that it
+     * does not use patterned encryption. However, if {@link #setPattern} is used to set the pattern
+     * to all zeroes, this will be interpreted as selecting the cbc1 scheme. The cbc1 scheme cannot
+     * successfully decrypt HLS SAMPLE-AES audio because of differences in how the IVs are handled.
+     * For this reason, it is recommended that a pattern of {@code 1} encrypted block and {@code 0}
+     * skip blocks be used with HLS SAMPLE-AES audio. This will trigger decryption to use cbcs mode
+     * while still decrypting every block.
      */
     public final static class CryptoInfo {
         /**
@@ -2377,11 +2421,13 @@
          */
         public int numSubSamples;
         /**
-         * The number of leading unencrypted bytes in each subSample.
+         * The number of leading unencrypted bytes in each subSample. If null, all bytes are treated
+         * as encrypted and {@link #numBytesOfEncryptedData} must be specified.
          */
         public int[] numBytesOfClearData;
         /**
-         * The number of trailing encrypted bytes in each subSample.
+         * The number of trailing encrypted bytes in each subSample. If null, all bytes are treated
+         * as clear and {@link #numBytesOfClearData} must be specified.
          */
         public int[] numBytesOfEncryptedData;
         /**
@@ -2400,35 +2446,34 @@
         public int mode;
 
         /**
-         * Metadata describing an encryption pattern for the protected bytes in
-         * a subsample.  An encryption pattern consists of a repeating sequence
-         * of crypto blocks comprised of a number of encrypted blocks followed
-         * by a number of unencrypted, or skipped, blocks.
+         * Metadata describing an encryption pattern for the protected bytes in a subsample.  An
+         * encryption pattern consists of a repeating sequence of crypto blocks comprised of a
+         * number of encrypted blocks followed by a number of unencrypted, or skipped, blocks.
          */
         public final static class Pattern {
             /**
-             * Number of blocks to be encrypted in the pattern. If zero, pattern
-             * encryption is inoperative.
+             * Number of blocks to be encrypted in the pattern. If both this and
+             * {@link #mSkipBlocks} are zero, pattern encryption is inoperative.
              */
             private int mEncryptBlocks;
 
             /**
-             * Number of blocks to be skipped (left clear) in the pattern. If zero,
-             * pattern encryption is inoperative.
+             * Number of blocks to be skipped (left clear) in the pattern. If both this and
+             * {@link #mEncryptBlocks} are zero, pattern encryption is inoperative.
              */
             private int mSkipBlocks;
 
             /**
-             * Construct a sample encryption pattern given the number of blocks to
-             * encrypt and skip in the pattern.
+             * Construct a sample encryption pattern given the number of blocks to encrypt and skip
+             * in the pattern. If both parameters are zero, pattern encryption is inoperative.
              */
             public Pattern(int blocksToEncrypt, int blocksToSkip) {
                 set(blocksToEncrypt, blocksToSkip);
             }
 
             /**
-             * Set the number of blocks to encrypt and skip in a sample encryption
-             * pattern.
+             * Set the number of blocks to encrypt and skip in a sample encryption pattern. If both
+             * parameters are zero, pattern encryption is inoperative.
              */
             public void set(int blocksToEncrypt, int blocksToSkip) {
                 mEncryptBlocks = blocksToEncrypt;
@@ -3528,6 +3573,8 @@
 
         private final static int TYPE_YUV = 1;
 
+        private final int mTransform = 0; //Default no transform
+
         @Override
         public int getFormat() {
             throwISEIfImageIsInvalid();
@@ -3547,6 +3594,12 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+            return mTransform;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
             return mTimestamp;
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 20c3209..234a4f4 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -40,6 +40,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * 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.
@@ -366,8 +367,8 @@
     MediaController2Provider createProvider(@NonNull Context context,
             @NonNull SessionToken2 token, @NonNull Executor executor,
             @NonNull ControllerCallback callback) {
-        return ApiLoader.getProvider(context)
-                .createMediaController2(context, this, token, executor, callback);
+        return ApiLoader.getProvider().createMediaController2(
+                context, this, token, executor, callback);
     }
 
     /**
@@ -735,8 +736,10 @@
         return mProvider.getPlaylistMetadata_impl();
     }
 
+
     /**
-     * Inserts the media item to the playlist at position index.
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
      * <p>
      * This will not change the currently playing media item.
      * If index is less than or equal to the current index of the playlist,
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b50aa47..3bfbcc2 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -105,10 +105,10 @@
  * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user
  *         for encoders, readable in the output format of decoders</b></td></tr>
- * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_COLUMNS}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * </table>
  */
 public final class MediaFormat {
@@ -150,17 +150,17 @@
      * The track's MediaFormat will come with {@link #KEY_WIDTH} and
      * {@link #KEY_HEIGHT} keys, which describes the width and height
      * of the image. If the image doesn't contain grid (i.e. none of
-     * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT},
-     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the
+     * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
      * track will contain a single sample of coded data for the entire image,
      * and the image width and height should be used to set up the decoder.
      *
      * If the image does come with grid, each sample from the track will
      * contain one tile in the grid, of which the size is described by
-     * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size
+     * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
      * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
      * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
-     * by {@link #KEY_GRID_COLS} samples in row-major, top-row first,
+     * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
      * left-to-right order. The output image should be reconstructed by
      * first tiling the decoding results of the tiles in the correct order,
      * then trimming (before rotation is applied) on the bottom and right
@@ -275,28 +275,28 @@
     public static final String KEY_FRAME_RATE = "frame-rate";
 
     /**
-     * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the width (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_WIDTH = "grid-width";
+    public static final String KEY_TILE_WIDTH = "tile-width";
 
     /**
-     * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the height (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
+     * @see #KEY_TILE_WIDTH
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_HEIGHT = "grid-height";
+    public static final String KEY_TILE_HEIGHT = "tile-height";
 
     /**
      * A key describing the number of grid rows in the content in a
@@ -304,9 +304,9 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
-     * @see #KEY_GRID_COLS
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
+     * @see #KEY_GRID_COLUMNS
      */
     public static final String KEY_GRID_ROWS = "grid-rows";
 
@@ -316,11 +316,11 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
      */
-    public static final String KEY_GRID_COLS = "grid-cols";
+    public static final String KEY_GRID_COLUMNS = "grid-cols";
 
     /**
      * A key describing the raw audio sample encoding/format.
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index b50c3e4..1967a1c 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * A class with information on a single media item with the metadata information.
  * Media item are application dependent so we cannot guarantee that they contain the right values.
  * <p>
@@ -81,7 +82,7 @@
     }
 
     public static MediaItem2 fromBundle(Context context, Bundle bundle) {
-        return ApiLoader.getProvider(context).fromBundle_MediaItem2(context, bundle);
+        return ApiLoader.getProvider().fromBundle_MediaItem2(context, bundle);
     }
 
     public String toString() {
@@ -164,8 +165,7 @@
          * @param flags
          */
         public Builder(@NonNull Context context, @Flags int flags) {
-            mProvider = ApiLoader.getProvider(context).createMediaItem2Builder(
-                    context, this, flags);
+            mProvider = ApiLoader.getProvider().createMediaItem2Builder(context, this, flags);
         }
 
         /**
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 6cab430..034d17e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -34,6 +34,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Base class for media library services.
  * <p>
  * Media library services enable applications to browse media content provided by an application
@@ -210,9 +211,8 @@
             public Builder(@NonNull MediaLibraryService2 service,
                     @NonNull @CallbackExecutor Executor callbackExecutor,
                     @NonNull MediaLibrarySessionCallback callback) {
-                super((instance) -> ApiLoader.getProvider(service)
-                        .createMediaLibraryService2Builder(service, (Builder) instance,
-                                callbackExecutor, callback));
+                super((instance) -> ApiLoader.getProvider().createMediaLibraryService2Builder(
+                        service, (Builder) instance, callbackExecutor, callback));
             }
 
             @Override
@@ -309,7 +309,7 @@
 
     @Override
     MediaSessionService2Provider createProvider() {
-        return ApiLoader.getProvider(this).createMediaLibraryService2(this);
+        return ApiLoader.getProvider().createMediaLibraryService2(this);
     }
 
     /**
@@ -403,7 +403,7 @@
          */
         public LibraryRoot(@NonNull Context context,
                 @NonNull String rootId, @Nullable Bundle extras) {
-            mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot(
+            mProvider = ApiLoader.getProvider().createMediaLibraryService2LibraryRoot(
                     context, this, rootId, extras);
         }
 
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index fb12065..1a15962 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -31,6 +31,7 @@
 import java.util.Set;
 
 /**
+ * @hide
  * Contains metadata about an item, such as the title, artist, etc.
  */
 // New version of MediaMetadata with following changes
@@ -684,7 +685,7 @@
      */
     public static @NonNull MediaMetadata2 fromBundle(@NonNull Context context,
             @Nullable Bundle bundle) {
-        return ApiLoader.getProvider(context).fromBundle_MediaMetadata2(context, bundle);
+        return ApiLoader.getProvider().fromBundle_MediaMetadata2(context, bundle);
     }
 
     /**
@@ -699,8 +700,7 @@
          * {@link MediaMetadata2} must be added.
          */
         public Builder(@NonNull Context context) {
-            mProvider = ApiLoader.getProvider(context).createMediaMetadata2Builder(
-                    context, this);
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this);
         }
 
         /**
@@ -711,8 +711,7 @@
          * @param source
          */
         public Builder(@NonNull Context context, @NonNull MediaMetadata2 source) {
-            mProvider = ApiLoader.getProvider(context).createMediaMetadata2Builder(
-                    context, this, source);
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this, source);
         }
 
         /**
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 1aeed6d..0955dd6 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -427,20 +427,40 @@
      *        a valid frame. The total number of frames available for retrieval can be queried
      *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the requested frame index does not exist.
      *
      * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
      *
+     * @see #getFrameAtIndex(int)
      * @see #getFramesAtIndex(int, int, BitmapParams)
+     * @see #getFramesAtIndex(int, int)
      */
-    public Bitmap getFrameAtIndex(int frameIndex, @Nullable BitmapParams params) {
+    public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) {
         List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
-        if (bitmaps == null || bitmaps.size() < 1) {
-            return null;
-        }
+        return bitmaps.get(0);
+    }
+
+    /**
+     * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param frameIndex 0-based index of the video frame. The frame index must be that of
+     *        a valid frame. The total number of frames available for retrieval can be queried
+     *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the requested frame index does not exist.
+     *
+     * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
+     * @see #getFramesAtIndex(int, int)
+     */
+    public Bitmap getFrameAtIndex(int frameIndex) {
+        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1);
         return bitmaps.get(0);
     }
 
@@ -461,7 +481,6 @@
      * @param numFrames number of consecutive video frames to retrieve. Must be a positive
      *        value. The stream must contain at least numFrames frames starting at frameIndex.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
@@ -471,8 +490,40 @@
      *         array could contain less frames than requested if the retrieval fails.
      *
      * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int)
      */
-    public List<Bitmap> getFramesAtIndex(
+    public @NonNull List<Bitmap> getFramesAtIndex(
+            int frameIndex, int numFrames, @NonNull BitmapParams params) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, params);
+    }
+
+    /**
+     * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
+     *        must be that of a valid frame. The total number of frames available for retrieval
+     *        can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     * @param numFrames number of consecutive video frames to retrieve. Must be a positive
+     *        value. The stream must contain at least numFrames frames starting at frameIndex.
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
+     *         stream doesn't contain at least numFrames starting at frameIndex.
+
+     * @return An list of Bitmaps containing the requested video frames. The returned
+     *         array could contain less frames than requested if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
+     */
+    public @NonNull List<Bitmap> getFramesAtIndex(int frameIndex, int numFrames) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, null);
+    }
+
+    private @NonNull List<Bitmap> getFramesAtIndexInternal(
             int frameIndex, int numFrames, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
             throw new IllegalStateException("Does not contail video or image sequences");
@@ -487,7 +538,8 @@
         }
         return _getFrameAtIndex(frameIndex, numFrames, params);
     }
-    private native List<Bitmap> _getFrameAtIndex(
+
+    private native @NonNull List<Bitmap> _getFrameAtIndex(
             int frameIndex, int numFrames, @Nullable BitmapParams params);
 
     /**
@@ -498,29 +550,39 @@
      * used to create the bitmap from the {@code BitmapParams} argument, for instance
      * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
      *
-     * @param imageIndex 0-based index of the image, with negative value indicating
-     *        the primary image.
+     * @param imageIndex 0-based index of the image.
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain still images.
      * @throws IllegalArgumentException if the requested image does not exist.
      *
      * @return the requested still image, or null if the image cannot be retrieved.
      *
+     * @see #getImageAtIndex(int)
      * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
      */
-    public Bitmap getImageAtIndex(int imageIndex, @Nullable BitmapParams params) {
-        if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
-            throw new IllegalStateException("Does not contail still images");
-        }
+    public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
+        return getImageAtIndexInternal(imageIndex, params);
+    }
 
-        String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
-        if (imageIndex >= Integer.parseInt(imageCount)) {
-            throw new IllegalArgumentException("Invalid image index: " + imageCount);
-        }
-
-        return _getImageAtIndex(imageIndex, params);
+    /**
+     * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param imageIndex 0-based index of the image.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     * @throws IllegalArgumentException if the requested image does not exist.
+     *
+     * @return the requested still image, or null if the image cannot be retrieved.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
+     */
+    public Bitmap getImageAtIndex(int imageIndex) {
+        return getImageAtIndexInternal(imageIndex, null);
     }
 
     /**
@@ -532,16 +594,46 @@
      * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
      *
      * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
-     *        If null, default config will be chosen.
      *
      * @return the primary image, or null if it cannot be retrieved.
      *
      * @throws IllegalStateException if the container doesn't contain still images.
      *
      * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage()
      */
-    public Bitmap getPrimaryImage(@Nullable BitmapParams params) {
-        return getImageAtIndex(-1, params);
+    public Bitmap getPrimaryImage(@NonNull BitmapParams params) {
+        return getImageAtIndexInternal(-1, params);
+    }
+
+    /**
+     * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @return the primary image, or null if it cannot be retrieved.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage(BitmapParams)
+     */
+    public Bitmap getPrimaryImage() {
+        return getImageAtIndexInternal(-1, null);
+    }
+
+    private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
+        if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
+            throw new IllegalStateException("Does not contail still images");
+        }
+
+        String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
+        if (imageIndex >= Integer.parseInt(imageCount)) {
+            throw new IllegalArgumentException("Invalid image index: " + imageCount);
+        }
+
+        return _getImageAtIndex(imageIndex, params);
     }
 
     private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params);
@@ -788,7 +880,8 @@
     public static final int METADATA_KEY_IMAGE_HEIGHT    = 30;
     /**
      * If the media contains still images, this key retrieves the rotation
-     * of the primary image.
+     * angle (in degrees clockwise) of the primary image. The image rotation
+     * angle must be one of 0, 90, 180, or 270 degrees.
      */
     public static final int METADATA_KEY_IMAGE_ROTATION  = 31;
     /**
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index ece19b9..dcc872c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -43,6 +43,7 @@
 
 
 /**
+ * @hide
  * MediaPlayer2 class can be used to control playback
  * of audio/video files and streams. An example on how to use the methods in
  * this class can be found in {@link android.widget.VideoView}.
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 48b7a51..1fcf02b 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -26,6 +26,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Base class for all media players that want media session.
  */
 public abstract class MediaPlayerBase implements AutoCloseable {
@@ -306,8 +307,9 @@
     public static abstract class PlayerEventCallback {
         /**
          * Called when the player's current data source has changed.
+         *
          * @param mpb the player whose data source changed.
-         * @param dsd the new current data source.
+         * @param dsd the new current data source. null, if no more data sources available.
          */
         public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
                 @Nullable DataSourceDesc dsd) { }
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
index 6b3620b..453e24a 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -30,6 +30,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
  * to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
  * notify methods to signal MediaSession2 that playlist-related state has changed.
@@ -148,7 +149,7 @@
     }
 
     public MediaPlaylistAgent(@NonNull Context context) {
-        mProvider = ApiLoader.getProvider(context).createMediaPlaylistAgent(context, this);
+        mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(context, this);
     }
 
     /**
@@ -228,10 +229,15 @@
     }
 
     /**
-     * Adds the media item to the playlist at the index
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
+     * <p>
+     * This will not change the currently playing media item.
+     * If index is less than or equal to the current index of the playlist,
+     * the current index of the playlist will be incremented correspondingly.
      *
-     * @param index index
-     * @param item media item to add
+     * @param index the index you want to add
+     * @param item the media item you want to add
      */
     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 6647831..0f4b5da 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -49,6 +49,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * 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>
@@ -431,16 +432,16 @@
         private final CommandProvider mProvider;
 
         public Command(@NonNull Context context, int commandCode) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2Command(this, commandCode, null, null);
+            mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                    this, commandCode, null, null);
         }
 
         public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extras) {
             if (action == null) {
                 throw new IllegalArgumentException("action shouldn't be null");
             }
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2Command(this, COMMAND_CODE_CUSTOM, action, extras);
+            mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                    this, COMMAND_CODE_CUSTOM, action, extras);
         }
 
         /**
@@ -488,7 +489,7 @@
          * @hide
          */
         public static Command fromBundle(@NonNull Context context, @NonNull Bundle command) {
-            return ApiLoader.getProvider(context).fromBundle_MediaSession2Command(context, command);
+            return ApiLoader.getProvider().fromBundle_MediaSession2Command(context, command);
         }
     }
 
@@ -499,13 +500,13 @@
         private final CommandGroupProvider mProvider;
 
         public CommandGroup(@NonNull Context context) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2CommandGroup(context, this, null);
+            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
+                    context, this, null);
         }
 
         public CommandGroup(@NonNull Context context, @Nullable CommandGroup others) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2CommandGroup(context, this, others);
+            mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
+                    context, this, others);
         }
 
         /**
@@ -559,8 +560,7 @@
          * @hide
          */
         public static @Nullable CommandGroup fromBundle(Context context, Bundle commands) {
-            return ApiLoader.getProvider(context)
-                    .fromBundle_MediaSession2CommandGroup(context, commands);
+            return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(context, commands);
         }
     }
 
@@ -1010,7 +1010,7 @@
     // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
     public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
         public Builder(Context context) {
-            super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
+            super((instance) -> ApiLoader.getProvider().createMediaSession2Builder(
                     context, (Builder) instance));
         }
 
@@ -1062,9 +1062,8 @@
          */
         public ControllerInfo(@NonNull Context context, int uid, int pid,
                 @NonNull String packageName, @NonNull IInterface callback) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2ControllerInfo(
-                            context, this, uid, pid, packageName, callback);
+            mProvider = ApiLoader.getProvider().createMediaSession2ControllerInfo(
+                    context, this, uid, pid, packageName, callback);
         }
 
         /**
@@ -1192,8 +1191,8 @@
             private final CommandButtonProvider.BuilderProvider mProvider;
 
             public Builder(@NonNull Context context) {
-                mProvider = ApiLoader.getProvider(context)
-                        .createMediaSession2CommandButtonBuilder(context, this);
+                mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(
+                        context, this);
             }
 
             public @NonNull Builder setCommand(@Nullable Command command) {
@@ -1543,7 +1542,7 @@
      * @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
      */
     public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
-        // TODO(jaewan): Implement (b/74090741).
+        mProvider.setOnDataSourceMissingHelper_impl(helper);
     }
 
     /**
@@ -1552,7 +1551,7 @@
      * @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
      */
     public void clearOnDataSourceMissingHelper() {
-        // TODO(jaewan): Implement (b/74090741)
+        mProvider.clearOnDataSourceMissingHelper_impl();
     }
 
     /**
@@ -1648,7 +1647,8 @@
     }
 
     /**
-     * Adds the media item to the playlist at position index.
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
      * <p>
      * This will not change the currently playing media item.
      * If index is less than or equal to the current index of the play list,
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 32caf4b..85ac9b2 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -30,6 +30,7 @@
 import android.os.IBinder;
 
 /**
+ * @hide
  * Base class for media session services, which is the service version of the {@link MediaSession2}.
  * <p>
  * It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
@@ -123,7 +124,7 @@
     }
 
     MediaSessionService2Provider createProvider() {
-        return ApiLoader.getProvider(this).createMediaSessionService2(this);
+        return ApiLoader.getProvider().createMediaSessionService2(this);
     }
 
     /**
@@ -220,9 +221,8 @@
          */
         public MediaNotification(@NonNull Context context,
                 int notificationId, @NonNull Notification notification) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSessionService2MediaNotification(
-                            context, this, notificationId, notification);
+            mProvider = ApiLoader.getProvider().createMediaSessionService2MediaNotification(
+                    context, this, notificationId, notification);
         }
 
         public int getNotificationId() {
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 29bd922..5f7a334 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -131,7 +131,7 @@
      * @return new Rating2 instance or {@code null} for error
      */
     public static Rating2 fromBundle(@NonNull Context context, @Nullable Bundle bundle) {
-        return ApiLoader.getProvider(context).fromBundle_Rating2(context, bundle);
+        return ApiLoader.getProvider().fromBundle_Rating2(context, bundle);
     }
 
     /**
@@ -154,7 +154,7 @@
      */
     public static @Nullable Rating2 newUnratedRating(@NonNull Context context,
             @Style int ratingStyle) {
-        return ApiLoader.getProvider(context).newUnratedRating_Rating2(context, ratingStyle);
+        return ApiLoader.getProvider().newUnratedRating_Rating2(context, ratingStyle);
     }
 
     /**
@@ -166,7 +166,7 @@
      * @return a new Rating2 instance.
      */
     public static @Nullable Rating2 newHeartRating(@NonNull Context context, boolean hasHeart) {
-        return ApiLoader.getProvider(context).newHeartRating_Rating2(context, hasHeart);
+        return ApiLoader.getProvider().newHeartRating_Rating2(context, hasHeart);
     }
 
     /**
@@ -178,7 +178,7 @@
      * @return a new Rating2 instance.
      */
     public static @Nullable Rating2 newThumbRating(@NonNull Context context, boolean thumbIsUp) {
-        return ApiLoader.getProvider(context).newThumbRating_Rating2(context, thumbIsUp);
+        return ApiLoader.getProvider().newThumbRating_Rating2(context, thumbIsUp);
     }
 
     /**
@@ -196,8 +196,7 @@
      */
     public static @Nullable Rating2 newStarRating(@NonNull Context context,
             @StarStyle int starRatingStyle, float starRating) {
-        return ApiLoader.getProvider(context).newStarRating_Rating2(
-                context, starRatingStyle, starRating);
+        return ApiLoader.getProvider().newStarRating_Rating2(context, starRatingStyle, starRating);
     }
 
     /**
@@ -209,7 +208,7 @@
      * @return null if the rating is out of range, a new Rating2 instance otherwise.
      */
     public static @Nullable Rating2 newPercentageRating(@NonNull Context context, float percent) {
-        return ApiLoader.getProvider(context).newPercentageRating_Rating2(context, percent);
+        return ApiLoader.getProvider().newPercentageRating_Rating2(context, percent);
     }
 
     /**
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index fdfa43a..f088be3 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
  * If it's representing a session service, it may not be ongoing.
  * <p>
@@ -80,7 +81,7 @@
      */
     public SessionToken2(@NonNull Context context, @NonNull String packageName,
             @NonNull String serviceName, int uid) {
-        mProvider = ApiLoader.getProvider(context).createSessionToken2(
+        mProvider = ApiLoader.getProvider().createSessionToken2(
                 context, this, packageName, serviceName, uid);
     }
 
@@ -150,7 +151,7 @@
      * @return
      */
     public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) {
-        return ApiLoader.getProvider(context).fromBundle_SessionToken2(context, bundle);
+        return ApiLoader.getProvider().fromBundle_SessionToken2(context, bundle);
     }
 
     /**
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 711f51f..2d96d096 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -26,6 +26,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * Handles requests to adjust or set the volume on a session. This is also used
  * to push volume updates back to the session. The provider must call
  * {@link #setCurrentVolume(int)} each time the volume being provided changes.
@@ -76,7 +77,7 @@
      */
     public VolumeProvider2(@NonNull Context context, @ControlType int controlType,
             int maxVolume, int currentVolume) {
-        mProvider = ApiLoader.getProvider(context).createVolumeProvider2(
+        mProvider = ApiLoader.getProvider().createVolumeProvider2(
                 context, this, controlType, maxVolume, currentVolume);
     }
 
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 963457b..56664a9 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -52,12 +52,13 @@
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
 
     // MediaSession2
-    boolean isTrusted(int uid, String packageName);
+    boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
     boolean createSession2(in Bundle sessionToken);
     void destroySession2(in Bundle sessionToken);
-    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
+    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
+            String packageName);
 
     void addSessionTokensListener(in ISessionTokensListener listener, int userId,
             String packageName);
-    void removeSessionTokensListener(in ISessionTokensListener listener);
+    void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 051321c..b7f49988 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -342,16 +342,17 @@
     /**
      * Returns whether the api
      *
-     * @param uid uid of the app
      * @param packageName packageName
+     * @param pid pid of the app
+     * @param uid uid of the app
      * @hide
      */
-    public boolean isTrusted(int uid, @NonNull String packageName) {
+    public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
         if (packageName == null) {
             return false;
         }
         try {
-            return mService.isTrusted(uid, packageName);
+            return mService.isTrusted(packageName, pid, uid);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
         }
@@ -390,6 +391,7 @@
     }
 
     /**
+     * @hide
      * Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
      * active sessions regardless of whether they're {@link MediaSession2} or
      * {@link MediaSessionService2}.
@@ -403,7 +405,8 @@
     public List<SessionToken2> getActiveSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
+                    mContext.getPackageName());
             return toTokenList(mContext, bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -412,6 +415,7 @@
     }
 
     /**
+     * @hide
      * Get {@link List} of {@link SessionToken2} for {@link MediaSessionService2} regardless of their
      * activeness. This list represents media apps that support background playback.
      * <p>
@@ -424,7 +428,8 @@
     public List<SessionToken2> getSessionServiceTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
+                    mContext.getPackageName());
             return toTokenList(mContext, bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -433,6 +438,7 @@
     }
 
     /**
+     * @hide
      * Get all {@link SessionToken2}s. This is the combined list of {@link #getActiveSessionTokens()}
      * and {@link #getSessionServiceTokens}.
      * <p>
@@ -447,7 +453,8 @@
     public List<SessionToken2> getAllSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
+                    mContext.getPackageName());
             return toTokenList(mContext, bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -456,6 +463,7 @@
     }
 
     /**
+     * @hide
      * Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
      * <p>
      * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
@@ -508,6 +516,7 @@
     }
 
     /**
+     * @hide
      * Stop receiving session token updates on the specified listener.
      *
      * @param listener The listener to remove.
@@ -521,7 +530,7 @@
             SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
             if (wrapper != null) {
                 try {
-                    mService.removeSessionTokensListener(wrapper.mStub);
+                    mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error in removeSessionTokensListener.", e);
                 } finally {
@@ -666,6 +675,7 @@
     }
 
     /**
+     * @hide
      * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
      * using {@link #addOnActiveSessionsChangedListener}.
      */
diff --git a/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl
new file mode 100644
index 0000000..ef4bb01
--- /dev/null
+++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.media.soundtrigger;
+
+import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+
+/**
+ * AIDL for the SoundTriggerDetectionService to run detection operations when triggered.
+ *
+ * {@hide}
+ */
+oneway interface ISoundTriggerDetectionService {
+    void setClient(in ParcelUuid uuid, in Bundle params, in ISoundTriggerDetectionServiceClient client);
+    void removeClient(in ParcelUuid uuid);
+    void onGenericRecognitionEvent(in ParcelUuid uuid, int opId, in SoundTrigger.GenericRecognitionEvent event);
+    void onError(in ParcelUuid uuid, int opId, int status);
+    void onStopOperation(in ParcelUuid uuid, int opId);
+}
diff --git a/wifi/java/android/net/wifi/ScanSettings.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
similarity index 61%
copy from wifi/java/android/net/wifi/ScanSettings.aidl
copy to media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
index ebd2a39..230735a 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.media.soundtrigger;
 
-parcelable ScanSettings;
+/**
+ * AIDL for the callbacks from a ISoundTriggerDetectionService.
+ *
+ * {@hide}
+ */
+oneway interface ISoundTriggerDetectionServiceClient {
+    void onOpFinished(int opId);
+}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java
new file mode 100644
index 0000000..7381d97
--- /dev/null
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java
@@ -0,0 +1,292 @@
+/*
+ * 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.media.soundtrigger;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.UUID;
+
+/**
+ * A service that allows interaction with the actual sound trigger detection on the system.
+ *
+ * <p> Sound trigger detection refers to detectors that match generic sound patterns that are
+ * not voice-based. The voice-based recognition models should utilize the {@link
+ * android.service.voice.VoiceInteractionService} instead. Access to this class needs to be
+ * protected by the {@value android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE}
+ * permission granted only to the system.
+ *
+ * <p>This service has to be explicitly started by an app, the system does not scan for and start
+ * these services.
+ *
+ * <p>If an operation ({@link #onGenericRecognitionEvent}, {@link #onError},
+ * {@link #onRecognitionPaused}, {@link #onRecognitionResumed}) is triggered the service is
+ * considered as running in the foreground. Once the operation is processed the service should call
+ * {@link #operationFinished(UUID, int)}. If this does not happen in
+ * {@link SoundTriggerManager#getDetectionServiceOperationsTimeout()} milliseconds
+ * {@link #onStopOperation(UUID, Bundle, int)} is called and the service is unbound.
+ *
+ * <p>The total amount of operations per day might be limited.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SoundTriggerDetectionService extends Service {
+    private static final String LOG_TAG = SoundTriggerDetectionService.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Client indexed by model uuid. This is needed for the {@link #operationFinished(UUID, int)}
+     * callbacks.
+     */
+    @GuardedBy("mLock")
+    private final ArrayMap<UUID, ISoundTriggerDetectionServiceClient> mClients =
+            new ArrayMap<>();
+
+    private Handler mHandler;
+
+    /**
+     * @hide
+     */
+    @Override
+    protected final void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+        mHandler = new Handler(base.getMainLooper());
+    }
+
+    private void setClient(@NonNull UUID uuid, @Nullable Bundle params,
+            @NonNull ISoundTriggerDetectionServiceClient client) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle setClient");
+
+        synchronized (mLock) {
+            mClients.put(uuid, client);
+        }
+        onConnected(uuid, params);
+    }
+
+    private void removeClient(@NonNull UUID uuid, @Nullable Bundle params) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle removeClient");
+
+        synchronized (mLock) {
+            mClients.remove(uuid);
+        }
+        onDisconnected(uuid, params);
+    }
+
+    /**
+     * The system has connected to this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p> This is called before any operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onConnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * The system has disconnected from this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p>Once this is called {@link #operationFinished} cannot be called anymore for
+     * {@code uuid}.
+     *
+     * <p> {@link #onConnected(UUID, Bundle)} is called before any further operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onDisconnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * A new generic sound trigger event has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param event The event that has been detected
+     */
+    @MainThread
+    public void onGenericRecognitionEvent(@NonNull UUID uuid, @Nullable Bundle params, int opId,
+            @NonNull SoundTrigger.RecognitionEvent event) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * A error has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param status The error code detected
+     */
+    @MainThread
+    public void onError(@NonNull UUID uuid, @Nullable Bundle params, int opId, int status) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * An operation took too long and should be stopped.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of the operation that took too long
+     */
+    @MainThread
+    public abstract void onStopOperation(@NonNull UUID uuid, @Nullable Bundle params, int opId);
+
+    /**
+     * Tell that the system that an operation has been fully processed.
+     *
+     * @param uuid The {@code uuid} of the model the recognition is registered for
+     * @param opId The id of the operation that is processed
+     */
+    public final void operationFinished(@Nullable UUID uuid, int opId) {
+        try {
+            ISoundTriggerDetectionServiceClient client;
+            synchronized (mLock) {
+                client = mClients.get(uuid);
+
+                if (client == null) {
+                    throw new IllegalStateException("operationFinished called, but no client for "
+                            + uuid + ". Was this called after onDisconnected?");
+                }
+            }
+            client.onOpFinished(opId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return new ISoundTriggerDetectionService.Stub() {
+            private final Object mBinderLock = new Object();
+
+            /** Cached params bundles indexed by the model uuid */
+            @GuardedBy("mBinderLock")
+            public final ArrayMap<UUID, Bundle> mParams = new ArrayMap<>();
+
+            @Override
+            public void setClient(ParcelUuid puuid, Bundle params,
+                    ISoundTriggerDetectionServiceClient client) {
+                UUID uuid = puuid.getUuid();
+                synchronized (mBinderLock) {
+                    mParams.put(uuid, params);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": setClient(" + params + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::setClient,
+                        SoundTriggerDetectionService.this, uuid, params, client));
+            }
+
+            @Override
+            public void removeClient(ParcelUuid puuid) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.remove(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": removeClient");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::removeClient,
+                        SoundTriggerDetectionService.this, uuid, params));
+            }
+
+            @Override
+            public void onGenericRecognitionEvent(ParcelUuid puuid, int opId,
+                    SoundTrigger.GenericRecognitionEvent event) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onGenericRecognitionEvent");
+                mHandler.sendMessage(
+                        obtainMessage(SoundTriggerDetectionService::onGenericRecognitionEvent,
+                                SoundTriggerDetectionService.this, uuid, params, opId, event));
+            }
+
+            @Override
+            public void onError(ParcelUuid puuid, int opId, int status) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onError(" + status + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onError,
+                        SoundTriggerDetectionService.this, uuid, params, opId, status));
+            }
+
+            @Override
+            public void onStopOperation(ParcelUuid puuid, int opId) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onStopOperation");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onStopOperation,
+                        SoundTriggerDetectionService.this, uuid, params, opId));
+            }
+        };
+    }
+
+    @CallSuper
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mClients.clear();
+
+        return false;
+    }
+}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 92ffae0..c9ec752 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -15,26 +15,31 @@
  */
 
 package android.media.soundtrigger;
+
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 
-import android.app.PendingIntent;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Slog;
 
 import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
 import java.util.UUID;
@@ -276,6 +281,40 @@
     }
 
     /**
+     * Starts recognition for the given model id. All events from the model will be sent to the
+     * service.
+     *
+     * <p>This only supports generic sound trigger events. For keyphrase events, please use
+     * {@link android.service.voice.VoiceInteractionService}.
+     *
+     * @param soundModelId Id of the sound model
+     * @param params Opaque data sent to each service call of the service as the {@code params}
+     *               argument
+     * @param detectionService The component name of the service that should receive the events.
+     *                         Needs to subclass {@link SoundTriggerDetectionService}
+     * @param config Configures the recognition
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the recognition could be started, error code
+     *         otherwise
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
+        @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
+        Preconditions.checkNotNull(soundModelId);
+        Preconditions.checkNotNull(detectionService);
+        Preconditions.checkNotNull(config);
+
+        try {
+            return mSoundTriggerService.startRecognitionForService(new ParcelUuid(soundModelId),
+                params, detectionService, config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Stops the given model's recognition.
      * @hide
      */
@@ -324,4 +363,19 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the amount of time (in milliseconds) an operation of the
+     * {@link ISoundTriggerDetectionService} is allowed to ask.
+     *
+     * @return The amount of time an sound trigger detection service operation is allowed to last
+     */
+    public int getDetectionServiceOperationsTimeout() {
+        try {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT);
+        } catch (Settings.SettingNotFoundException e) {
+            return Integer.MAX_VALUE;
+        }
+    }
 }
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
index cf18bdd..6f82f68 100644
--- a/media/java/android/media/update/ApiLoader.java
+++ b/media/java/android/media/update/ApiLoader.java
@@ -46,11 +46,6 @@
 
     private ApiLoader() { }
 
-    @Deprecated
-    public static StaticProvider getProvider(Context context) {
-        return getProvider();
-    }
-
     public static StaticProvider getProvider() {
         if (sMediaUpdatable != null) return sMediaUpdatable;
 
@@ -79,7 +74,7 @@
             ActivityManager.getService().addPackageDependency(ai.packageName);
         }
 
-        PathClassLoader classLoader = new PathClassLoader(ai.sourceDir,
+        ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
                 ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
                 ClassLoader.getSystemClassLoader().getParent());
         return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index e5ea386..0faed9d 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -21,7 +21,6 @@
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
@@ -29,6 +28,7 @@
 import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
@@ -68,6 +68,8 @@
     int getPlayerState_impl();
     long getPosition_impl();
     long getBufferedPosition_impl();
+    void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
+    void clearOnDataSourceMissingHelper_impl();
 
     interface CommandProvider {
         int getCommandCode_impl();
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index f5311764..bfb3ea2 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -45,6 +45,7 @@
 #define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID       "mNativeContext"
 #define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID   "mNativeBuffer"
 #define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID       "mTimestamp"
+#define ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID       "mTransform"
 
 #define CONSUMER_BUFFER_USAGE_UNKNOWN              0;
 // ----------------------------------------------------------------------------
@@ -66,6 +67,7 @@
 static struct {
     jfieldID mNativeBuffer;
     jfieldID mTimestamp;
+    jfieldID mTransform;
     jfieldID mPlanes;
 } gSurfaceImageClassInfo;
 
@@ -307,6 +309,12 @@
                         "can't find android/graphics/ImageReader.%s",
                         ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
 
+    gSurfaceImageClassInfo.mTransform = env->GetFieldID(
+            imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID, "I");
+    LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mTransform == NULL,
+                        "can't find android/graphics/ImageReader.%s",
+                        ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID);
+
     gSurfaceImageClassInfo.mPlanes = env->GetFieldID(
             imageClazz, "mPlanes", "[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;");
     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL,
@@ -596,6 +604,8 @@
     Image_setBufferItem(env, image, buffer);
     env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
             static_cast<jlong>(buffer->mTimestamp));
+    env->SetIntField(image, gSurfaceImageClassInfo.mTransform,
+            static_cast<jint>(buffer->mTransform));
 
     return ACQUIRE_SUCCESS;
 }
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 2c74992..2b8f9f89 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -421,7 +421,7 @@
 }
 
 static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
-        jlong timestampNs, jint left, jint top, jint right, jint bottom) {
+        jlong timestampNs, jint left, jint top, jint right, jint bottom, jint transform) {
     ALOGV("%s", __FUNCTION__);
     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
     if (ctx == NULL || thiz == NULL) {
@@ -465,6 +465,12 @@
         return;
     }
 
+    res = native_window_set_buffers_transform(anw.get(), transform);
+    if (res != OK) {
+        jniThrowRuntimeException(env, "Set transform failed");
+        return;
+    }
+
     // Finally, queue input buffer
     res = anw->queueBuffer(anw.get(), buffer, fenceFd);
     if (res != OK) {
@@ -487,7 +493,7 @@
 
 static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
         jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
-        jint right, jint bottom) {
+        jint right, jint bottom, jint transform) {
     ALOGV("%s", __FUNCTION__);
     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
     if (ctx == NULL || thiz == NULL) {
@@ -530,7 +536,7 @@
     }
     sp < ANativeWindow > anw = surface;
 
-    // Step 2. Set timestamp and crop. Note that we do not need unlock the image because
+    // Step 2. Set timestamp, crop and transform. Note that we do not need unlock the image because
     // it was not locked.
     ALOGV("timestamp to be queued: %" PRId64, timestampNs);
     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
@@ -550,6 +556,12 @@
         return res;
     }
 
+    res = native_window_set_buffers_transform(anw.get(), transform);
+    if (res != OK) {
+        jniThrowRuntimeException(env, "Set transform failed");
+        return res;
+    }
+
     // Step 3. Queue Image.
     res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
             -1);
@@ -785,9 +797,9 @@
     {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;II)J",
                                                               (void*)ImageWriter_init },
     {"nativeClose",              "(J)V",                      (void*)ImageWriter_close },
-    {"nativeAttachAndQueueImage", "(JJIJIIII)I",          (void*)ImageWriter_attachAndQueueImage },
+    {"nativeAttachAndQueueImage", "(JJIJIIIII)I",          (void*)ImageWriter_attachAndQueueImage },
     {"nativeDequeueInputImage", "(JLandroid/media/Image;)V",  (void*)ImageWriter_dequeueImage },
-    {"nativeQueueInputImage",   "(JLandroid/media/Image;JIIII)V",  (void*)ImageWriter_queueImage },
+    {"nativeQueueInputImage",   "(JLandroid/media/Image;JIIIII)V",  (void*)ImageWriter_queueImage },
     {"cancelImage",             "(JLandroid/media/Image;)V",   (void*)ImageWriter_cancelImage },
 };
 
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 3a67142..4f6763e 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -449,13 +449,14 @@
     std::vector<sp<IMemory> > frames;
     status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat);
     if (err != OK || frames.size() == 0) {
-        ALOGE("failed to get frames from retriever, err=%d, size=%zu",
-                err, frames.size());
+        jniThrowException(env,
+                "java/lang/IllegalStateException", "No frames from retriever");
         return NULL;
     }
     jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit);
     if (arrayList == NULL) {
-        ALOGE("can't create bitmap array list object");
+        jniThrowException(env,
+                "java/lang/IllegalStateException", "Can't create bitmap array");
         return NULL;
     }
 
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 23ef84f6..12d7440 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -39,6 +39,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <jni.h>
+#include <media/stagefright/NuMediaExtractor.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -788,6 +789,41 @@
     return exif_get_long(e->data, o);
 }
 
+static ExifData *getExifFromExtractor(const char *path) {
+    std::unique_ptr<uint8_t[]> exifBuf;
+    ExifData *exifdata = NULL;
+
+    FILE *fp = fopen (path, "rb");
+    if (!fp) {
+        ALOGE("failed to open file");
+        return NULL;
+    }
+
+    sp<NuMediaExtractor> extractor = new NuMediaExtractor();
+    fseek(fp, 0L, SEEK_END);
+    if (extractor->setDataSource(fileno(fp), 0, ftell(fp)) != OK) {
+        ALOGE("failed to setDataSource");
+        fclose(fp);
+        return NULL;
+    }
+
+    off64_t offset;
+    size_t size;
+    if (extractor->getExifOffsetSize(&offset, &size) != OK) {
+        fclose(fp);
+        return NULL;
+    }
+
+    exifBuf.reset(new uint8_t[size]);
+    fseek(fp, offset, SEEK_SET);
+    if (fread(exifBuf.get(), 1, size, fp) == size) {
+        exifdata = exif_data_new_from_data(exifBuf.get(), size);
+    }
+
+    fclose(fp);
+    return exifdata;
+}
+
 MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                              MtpObjectInfo& info) {
     MtpString       path;
@@ -834,7 +870,12 @@
         case MTP_FORMAT_EXIF_JPEG:
         case MTP_FORMAT_HEIF:
         case MTP_FORMAT_JFIF: {
-            ExifData *exifdata = exif_data_new_from_file(path);
+            ExifData *exifdata;
+            if (info.mFormat == MTP_FORMAT_HEIF) {
+                exifdata = getExifFromExtractor(path);
+            } else {
+                exifdata = exif_data_new_from_file(path);
+            }
             if (exifdata) {
                 if ((false)) {
                     exif_data_foreach_content(exifdata, foreachcontent, NULL);
@@ -892,7 +933,12 @@
             case MTP_FORMAT_EXIF_JPEG:
             case MTP_FORMAT_HEIF:
             case MTP_FORMAT_JFIF: {
-                ExifData *exifdata = exif_data_new_from_file(path);
+                ExifData *exifdata;
+                if (format == MTP_FORMAT_HEIF) {
+                    exifdata = getExifFromExtractor(path);
+                } else {
+                    exifdata = exif_data_new_from_file(path);
+                }
                 if (exifdata) {
                     if (exifdata->data) {
                         result = malloc(exifdata->size);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index fe785c2..1a9ce95 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -150,6 +150,13 @@
     <!-- Bluetooth settings.  Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
 
+    <!-- Connected devices settings. Message when Bluetooth is connected and active, showing remote device status and battery level. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_active_battery_level">Active, <xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+    <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+    <!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_active_no_battery_level">Active</string>
+
     <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the A2DP profile. -->
     <string name="bluetooth_profile_a2dp">Media audio</string>
     <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the headset or handsfree profile. -->
@@ -1051,6 +1058,9 @@
     <!-- About phone, status item value if the actual value is not available. -->
     <string name="status_unavailable">Unavailable</string>
 
+    <!-- About phone, status value for MAC address when MAC randomization feature is enabled and the device is disconnected. -->
+    <string name="wifi_status_mac_randomized">MAC is randomized</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>
@@ -1090,4 +1100,7 @@
     <string name="zen_mode_duration_settings_title">Duration</string>
     <!-- Do not disturb: Duration option to always prompt for the duration of dnd -->
     <string name="zen_mode_duration_always_prompt_title">Ask every time</string>
+
+    <!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
+    <string name="time_unit_just_now">Just now</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
index 8e29e50..d39ec4b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
@@ -15,6 +15,9 @@
  */
 package com.android.settingslib;
 
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
@@ -80,6 +83,7 @@
     protected void onBindDialogView(View view) {
         final EditText editText = view.findViewById(android.R.id.edit);
         if (editText != null) {
+            editText.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
             editText.requestFocus();
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 350b648..8473c06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.UserHandle;
@@ -50,6 +51,19 @@
         return getEnabledServicesFromSettings(context, UserHandle.myUserId());
     }
 
+    public static boolean hasServiceCrashed(String packageName, String serviceName,
+            List<AccessibilityServiceInfo> enabledServiceInfos) {
+        for (int i = 0; i < enabledServiceInfos.size(); i++) {
+            AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i);
+            final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo;
+            if (TextUtils.equals(serviceInfo.packageName, packageName)
+                    && TextUtils.equals(serviceInfo.name, serviceName)) {
+                return accessibilityServiceInfo.crashed;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return the set of enabled accessibility services for {@param userId}. If there are no
      * services, it returns the unmodifiable {@link Collections#emptySet()}.
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 3a0ae9f..093b1bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.applications;
 
+import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.Application;
@@ -59,6 +60,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.Collator;
 import java.text.Normalizer;
 import java.text.Normalizer.Form;
@@ -135,6 +138,38 @@
     final BackgroundHandler mBackgroundHandler;
     final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
 
+    /** Requests that the home app is loaded. */
+    public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
+
+    /** Requests that icons are loaded. */
+    public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
+
+    /** Requests that sizes are loaded. */
+    public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
+
+    /** Requests that launcher intents are resolved. */
+    public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
+
+    /** Requests that leanback launcher intents are resolved. */
+    public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
+
+    /**
+     * Flags to configure the session to request various types of info.
+     */
+    @IntDef(prefix = { "FLAG_SESSION_" }, value = {
+            FLAG_SESSION_REQUEST_HOME_APP,
+            FLAG_SESSION_REQUEST_ICONS,
+            FLAG_SESSION_REQUEST_SIZES,
+            FLAG_SESSION_REQUEST_LAUNCHER,
+            FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionFlags {}
+
+    public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+            FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
+            FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+
     private ApplicationsState(Application app) {
         mContext = app;
         mPm = mContext.getPackageManager();
@@ -265,7 +300,8 @@
         }
     }
 
-    private void clearEntries() {
+    @VisibleForTesting
+    void clearEntries() {
         for (int i = 0; i < mEntriesMap.size(); i++) {
             mEntriesMap.valueAt(i).clear();
         }
@@ -346,7 +382,7 @@
         if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
         synchronized (mEntriesMap) {
             AppEntry entry = mEntriesMap.get(userId).get(packageName);
-            if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+            if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
                 mBackgroundHandler.post(
                         () -> {
                             try {
@@ -595,6 +631,7 @@
     }
 
     public class Session implements LifecycleObserver {
+
         final Callbacks mCallbacks;
         boolean mResumed;
 
@@ -609,6 +646,7 @@
         boolean mRebuildForeground;
 
         private final boolean mHasLifecycle;
+        @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
 
         Session(Callbacks callbacks, Lifecycle lifecycle) {
             mCallbacks = callbacks;
@@ -620,6 +658,14 @@
             }
         }
 
+        public @SessionFlags int getSessionFlags() {
+            return mFlags;
+        }
+
+        public void setSessionFlags(@SessionFlags int flags) {
+            mFlags = flags;
+        }
+
         @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
         public void onResume() {
             if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
@@ -833,10 +879,11 @@
     private class BackgroundHandler extends Handler {
         static final int MSG_REBUILD_LIST = 1;
         static final int MSG_LOAD_ENTRIES = 2;
-        static final int MSG_LOAD_ICONS = 3;
-        static final int MSG_LOAD_SIZES = 4;
-        static final int MSG_LOAD_LAUNCHER = 5;
-        static final int MSG_LOAD_HOME_APP = 6;
+        static final int MSG_LOAD_HOME_APP = 3;
+        static final int MSG_LOAD_LAUNCHER = 4;
+        static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
+        static final int MSG_LOAD_ICONS = 6;
+        static final int MSG_LOAD_SIZES = 7;
 
         boolean mRunning;
 
@@ -860,6 +907,8 @@
                 }
             }
 
+            int flags = getCombinedSessionFlags(mSessions);
+
             switch (msg.what) {
                 case MSG_REBUILD_LIST: {
                 } break;
@@ -889,8 +938,8 @@
                                 // happens because of the way we generate the list in
                                 // doResumeIfNeededLocked.
                                 AppEntry entry = mEntriesMap.get(0).get(info.packageName);
-                                if (entry != null &&
-                                        (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                                if (entry != null && !hasFlag(entry.info.flags,
+                                        ApplicationInfo.FLAG_INSTALLED)) {
                                     mEntriesMap.get(0).remove(info.packageName);
                                     mAppEntries.remove(entry);
                                 }
@@ -909,166 +958,206 @@
                     }
                 } break;
                 case MSG_LOAD_HOME_APP: {
-                    final List<ResolveInfo> homeActivities = new ArrayList<>();
-                    mPm.getHomeActivities(homeActivities);
-                    synchronized (mEntriesMap) {
-                        final int entryCount = mEntriesMap.size();
-                        for (int i = 0; i < entryCount; i++) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
-                            final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
-                            for (ResolveInfo activity : homeActivities) {
-                                String packageName = activity.activityInfo.packageName;
-                                AppEntry entry = userEntries.get(packageName);
-                                if (entry != null) {
-                                    entry.isHomeApp = true;
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
+                        final List<ResolveInfo> homeActivities = new ArrayList<>();
+                        mPm.getHomeActivities(homeActivities);
+                        synchronized (mEntriesMap) {
+                            final int entryCount = mEntriesMap.size();
+                            for (int i = 0; i < entryCount; i++) {
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+                                final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
+                                        i);
+                                for (ResolveInfo activity : homeActivities) {
+                                    String packageName = activity.activityInfo.packageName;
+                                    AppEntry entry = userEntries.get(packageName);
+                                    if (entry != null) {
+                                        entry.isHomeApp = true;
+                                    }
                                 }
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
                             }
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
                         }
                     }
                     sendEmptyMessage(MSG_LOAD_LAUNCHER);
-                }
-                break;
-                case MSG_LOAD_LAUNCHER: {
-                    Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
-                            .addCategory(Intent.CATEGORY_LAUNCHER);
-                    for (int i = 0; i < mEntriesMap.size(); i++) {
-                        int userId = mEntriesMap.keyAt(i);
-                        // If we do not specify MATCH_DIRECT_BOOT_AWARE or
-                        // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
-                        // according to the user's lock state. When the user is locked, components
-                        // with ComponentInfo#directBootAware == false will be filtered. We should
-                        // explicitly include both direct boot aware and unaware components here.
-                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
-                                launchIntent,
-                                PackageManager.MATCH_DISABLED_COMPONENTS
-                                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                                userId
-                        );
-                        synchronized (mEntriesMap) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
-                            HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
-                            final int N = intents.size();
-                            for (int j = 0; j < N; j++) {
-                                String packageName = intents.get(j).activityInfo.packageName;
-                                AppEntry entry = userEntries.get(packageName);
-                                if (entry != null) {
-                                    entry.hasLauncherEntry = true;
-                                } else {
-                                    Log.w(TAG, "Cannot find pkg: " + packageName
-                                            + " on user " + userId);
+                } break;
+                case MSG_LOAD_LAUNCHER:
+                case MSG_LOAD_LEANBACK_LAUNCHER: {
+                    if ((msg.what == MSG_LOAD_LAUNCHER &&
+                            hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
+                            || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
+                            hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
+
+                        Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+                        launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
+                                ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
+                        for (int i = 0; i < mEntriesMap.size(); i++) {
+                            int userId = mEntriesMap.keyAt(i);
+                            // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+                            // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+                            // according to the user's lock state. When the user is locked,
+                            // components
+                            // with ComponentInfo#directBootAware == false will be filtered. We should
+                            // explicitly include both direct boot aware and unaware components here.
+                            List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+                                    launchIntent,
+                                    PackageManager.MATCH_DISABLED_COMPONENTS
+                                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                    userId
+                            );
+                            synchronized (mEntriesMap) {
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+                                HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+                                final int N = intents.size();
+                                for (int j = 0; j < N; j++) {
+                                    ResolveInfo resolveInfo = intents.get(j);
+                                    String packageName = resolveInfo.activityInfo.packageName;
+                                    AppEntry entry = userEntries.get(packageName);
+                                    if (entry != null) {
+                                        entry.hasLauncherEntry = true;
+                                        entry.launcherEntryEnabled |=
+                                                resolveInfo.activityInfo.enabled;
+                                    } else {
+                                        Log.w(TAG, "Cannot find pkg: " + packageName
+                                                + " on user " + userId);
+                                    }
                                 }
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
                             }
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+                        }
+
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+                            mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
                         }
                     }
-
-                    if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
-                        mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+                    if (msg.what == MSG_LOAD_LAUNCHER) {
+                        sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
+                    } else {
+                        sendEmptyMessage(MSG_LOAD_ICONS);
                     }
-                    sendEmptyMessage(MSG_LOAD_ICONS);
                 } break;
                 case MSG_LOAD_ICONS: {
-                    int numDone = 0;
-                    synchronized (mEntriesMap) {
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
-                        for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
-                            AppEntry entry = mAppEntries.get(i);
-                            if (entry.icon == null || !entry.mounted) {
-                                synchronized (entry) {
-                                    if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
+                        int numDone = 0;
+                        synchronized (mEntriesMap) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+                            for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
+                                AppEntry entry = mAppEntries.get(i);
+                                if (entry.icon == null || !entry.mounted) {
+                                    synchronized (entry) {
+                                        if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+                                            if (!mRunning) {
+                                                mRunning = true;
+                                                Message m = mMainHandler.obtainMessage(
+                                                        MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+                                                mMainHandler.sendMessage(m);
+                                            }
+                                            numDone++;
+                                        }
+                                    }
+                                }
+                            }
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+                        }
+                        if (numDone > 0) {
+                            if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+                                mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+                            }
+                        }
+                        if (numDone >= 2) {
+                            sendEmptyMessage(MSG_LOAD_ICONS);
+                            break;
+                        }
+                    }
+                    sendEmptyMessage(MSG_LOAD_SIZES);
+                } break;
+                case MSG_LOAD_SIZES: {
+                    if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
+                        synchronized (mEntriesMap) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+                            if (mCurComputingSizePkg != null) {
+                                if (DEBUG_LOCKING) Log.v(TAG,
+                                        "MSG_LOAD_SIZES releasing: currently computing");
+                                return;
+                            }
+
+                            long now = SystemClock.uptimeMillis();
+                            for (int i = 0; i < mAppEntries.size(); i++) {
+                                AppEntry entry = mAppEntries.get(i);
+                                if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
+                                        && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
+                                    if (entry.sizeLoadStart == 0 ||
+                                            (entry.sizeLoadStart < (now - 20 * 1000))) {
                                         if (!mRunning) {
                                             mRunning = true;
                                             Message m = mMainHandler.obtainMessage(
                                                     MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
                                             mMainHandler.sendMessage(m);
                                         }
-                                        numDone++;
+                                        entry.sizeLoadStart = now;
+                                        mCurComputingSizeUuid = entry.info.storageUuid;
+                                        mCurComputingSizePkg = entry.info.packageName;
+                                        mCurComputingSizeUserId = UserHandle.getUserId(
+                                                entry.info.uid);
+
+                                        mBackgroundHandler.post(() -> {
+                                            try {
+                                                final StorageStats stats =
+                                                        mStats.queryStatsForPackage(
+                                                                mCurComputingSizeUuid,
+                                                                mCurComputingSizePkg,
+                                                                UserHandle.of(
+                                                                        mCurComputingSizeUserId));
+                                                final PackageStats legacy = new PackageStats(
+                                                        mCurComputingSizePkg,
+                                                        mCurComputingSizeUserId);
+                                                legacy.codeSize = stats.getCodeBytes();
+                                                legacy.dataSize = stats.getDataBytes();
+                                                legacy.cacheSize = stats.getCacheBytes();
+                                                try {
+                                                    mStatsObserver.onGetStatsCompleted(legacy,
+                                                            true);
+                                                } catch (RemoteException ignored) {
+                                                }
+                                            } catch (NameNotFoundException | IOException e) {
+                                                Log.w(TAG, "Failed to query stats: " + e);
+                                                try {
+                                                    mStatsObserver.onGetStatsCompleted(null, false);
+                                                } catch (RemoteException ignored) {
+                                                }
+                                            }
+
+                                        });
                                     }
+                                    if (DEBUG_LOCKING) Log.v(TAG,
+                                            "MSG_LOAD_SIZES releasing: now computing");
+                                    return;
                                 }
                             }
-                        }
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
-                    }
-                    if (numDone > 0) {
-                        if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
-                            mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
-                        }
-                    }
-                    if (numDone >= 2) {
-                        sendEmptyMessage(MSG_LOAD_ICONS);
-                    } else {
-                        sendEmptyMessage(MSG_LOAD_SIZES);
-                    }
-                } break;
-                case MSG_LOAD_SIZES: {
-                    synchronized (mEntriesMap) {
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
-                        if (mCurComputingSizePkg != null) {
-                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
-                            return;
-                        }
-
-                        long now = SystemClock.uptimeMillis();
-                        for (int i=0; i<mAppEntries.size(); i++) {
-                            AppEntry entry = mAppEntries.get(i);
-                            if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
-                                    && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
-                                if (entry.sizeLoadStart == 0 ||
-                                        (entry.sizeLoadStart < (now-20*1000))) {
-                                    if (!mRunning) {
-                                        mRunning = true;
-                                        Message m = mMainHandler.obtainMessage(
-                                                MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
-                                        mMainHandler.sendMessage(m);
-                                    }
-                                    entry.sizeLoadStart = now;
-                                    mCurComputingSizeUuid = entry.info.storageUuid;
-                                    mCurComputingSizePkg = entry.info.packageName;
-                                    mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-
-                                    mBackgroundHandler.post(() -> {
-                                        try {
-                                            final StorageStats stats = mStats.queryStatsForPackage(
-                                                    mCurComputingSizeUuid, mCurComputingSizePkg,
-                                                    UserHandle.of(mCurComputingSizeUserId));
-                                            final PackageStats legacy = new PackageStats(
-                                                    mCurComputingSizePkg, mCurComputingSizeUserId);
-                                            legacy.codeSize = stats.getCodeBytes();
-                                            legacy.dataSize = stats.getDataBytes();
-                                            legacy.cacheSize = stats.getCacheBytes();
-                                            try {
-                                                mStatsObserver.onGetStatsCompleted(legacy, true);
-                                            } catch (RemoteException ignored) {
-                                            }
-                                        } catch (NameNotFoundException | IOException e) {
-                                            Log.w(TAG, "Failed to query stats: " + e);
-                                            try {
-                                                mStatsObserver.onGetStatsCompleted(null, false);
-                                            } catch (RemoteException ignored) {
-                                            }
-                                        }
-
-                                    });
-                                }
-                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
-                                return;
+                            if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+                                mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+                                mRunning = false;
+                                Message m = mMainHandler.obtainMessage(
+                                        MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+                                mMainHandler.sendMessage(m);
                             }
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
                         }
-                        if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
-                            mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
-                            mRunning = false;
-                            Message m = mMainHandler.obtainMessage(
-                                    MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
-                            mMainHandler.sendMessage(m);
-                        }
-                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
                     }
                 } break;
             }
         }
 
+        private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+            synchronized (mEntriesMap) {
+                int flags = 0;
+                for (Session session : sessions) {
+                    flags |= session.mFlags;
+                }
+                return flags;
+            }
+        }
+
         final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
             public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
                 if (!succeeded) {
@@ -1257,6 +1346,11 @@
         public boolean hasLauncherEntry;
 
         /**
+         * Whether the component that has a launcher intent filter is enabled.
+         */
+        public boolean launcherEntryEnabled;
+
+        /**
          * Whether or not it's a Home app.
          */
         public boolean isHomeApp;
@@ -1283,7 +1377,7 @@
         // A location where extra info can be placed to be used by custom filters.
         public Object extraInfo;
 
-        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
         public AppEntry(Context context, ApplicationInfo info, long id) {
             apkFile = new File(info.sourceDir);
             this.id = id;
@@ -1336,6 +1430,10 @@
         }
     }
 
+    private static boolean hasFlag(int flags, int flag) {
+        return (flags & flag) != 0;
+    }
+
     /**
      * Compare by label, then package name, then uid.
      */
@@ -1449,13 +1547,13 @@
         public boolean filterApp(AppEntry entry) {
             if (AppUtils.isInstant(entry.info)) {
                 return false;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
                 return true;
             } else if (entry.hasLauncherEntry) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+            } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
                 return true;
             }
             return false;
@@ -1486,9 +1584,9 @@
 
         @Override
         public boolean filterApp(AppEntry entry) {
-            if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
                 return true;
-            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
                 return true;
             }
             return false;
@@ -1547,7 +1645,7 @@
         @Override
         public boolean filterApp(AppEntry entry) {
             return !AppUtils.isInstant(entry.info)
-                && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+                && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
         }
     };
 
@@ -1589,7 +1687,7 @@
             // TODO: Update for the new game category.
             boolean isGame;
             synchronized (info.info) {
-                isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+                isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
                         || info.info.category == ApplicationInfo.CATEGORY_GAME;
             }
             return isGame;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 784c714..b74b2cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -115,6 +116,8 @@
                    new ActiveDeviceChangedHandler());
         addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
                    new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
 
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
@@ -434,6 +437,8 @@
                 bluetoothProfile = BluetoothProfile.A2DP;
             } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
                 bluetoothProfile = BluetoothProfile.HEADSET;
+            } else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.HEARING_AID;
             } else {
                 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
                 return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f6ec6a8..6c068ff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -109,6 +109,7 @@
     // Active device state
     private boolean mIsActiveDeviceA2dp = false;
     private boolean mIsActiveDeviceHeadset = false;
+    private boolean mIsActiveDeviceHearingAid = false;
 
     /**
      * Describes the current device and profile for logging.
@@ -416,6 +417,36 @@
         }
     }
 
+    /**
+     * Set this device as active device
+     * @return true if at least one profile on this device is set to active, false otherwise
+     */
+    public boolean setActive() {
+        boolean result = false;
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null && isConnectedProfile(a2dpProfile)) {
+            if (a2dpProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + this);
+                result = true;
+            }
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if ((headsetProfile != null) && isConnectedProfile(headsetProfile)) {
+            if (headsetProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + this);
+                result = true;
+            }
+        }
+        HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+        if ((hearingAidProfile != null) && isConnectedProfile(hearingAidProfile)) {
+            if (hearingAidProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: Hearing Aid active device=" + this);
+                result = true;
+            }
+        }
+        return result;
+    }
+
     void refreshName() {
         fetchName();
         dispatchAttributesChanged();
@@ -478,6 +509,10 @@
             changed = (mIsActiveDeviceHeadset != isActive);
             mIsActiveDeviceHeadset = isActive;
             break;
+        case BluetoothProfile.HEARING_AID:
+            changed = (mIsActiveDeviceHearingAid != isActive);
+            mIsActiveDeviceHearingAid = isActive;
+            break;
         default:
             Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
                     " isActive " + isActive);
@@ -501,6 +536,8 @@
                 return mIsActiveDeviceA2dp;
             case BluetoothProfile.HEADSET:
                 return mIsActiveDeviceHeadset;
+            case BluetoothProfile.HEARING_AID:
+                return mIsActiveDeviceHearingAid;
             default:
                 Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
                 break;
@@ -592,6 +629,10 @@
         if (headsetProfile != null) {
             mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
         }
+        HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+        if (hearingAidProfile != null) {
+            mIsActiveDeviceHearingAid = hearingAidProfile.isActiveDevice(mDevice);
+        }
     }
 
     /**
@@ -922,6 +963,7 @@
         boolean profileConnected = false;       // at least one profile is connected
         boolean a2dpNotConnected = false;       // A2DP is preferred but not connected
         boolean hfpNotConnected = false;    // HFP is preferred but not connected
+        boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
 
         for (LocalBluetoothProfile profile : getProfiles()) {
             int connectionStatus = getProfileConnectionState(profile);
@@ -943,6 +985,8 @@
                         } else if ((profile instanceof HeadsetProfile) ||
                                    (profile instanceof HfpClientProfile)) {
                             hfpNotConnected = true;
+                        } else if (profile instanceof  HearingAidProfile) {
+                            hearingAidNotConnected = true;
                         }
                     }
                     break;
@@ -975,6 +1019,10 @@
                 activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
             }
         }
+        if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
+            activeDeviceString = activeDeviceStringsArray[1];
+            return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+        }
 
         if (profileConnected) {
             if (a2dpNotConnected && hfpNotConnected) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 8f9e4635..920500f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -134,6 +134,17 @@
         return mService.getConnectionState(device);
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        mService.setActiveDevice(device);
+        return true;
+    }
+
+    public boolean isActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.isActiveDevice(device);
+    }
+
     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/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 3c02f6a..2d3dfe4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -59,7 +59,8 @@
 
     @Override
     public boolean isAvailable() {
-        return mContext.getSystemService(UserManager.class).isAdminUser();
+        final UserManager um = mContext.getSystemService(UserManager.class);
+        return um != null && (um.isAdminUser() || um.isDemoUser());
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
index 85bf4e8..d21da4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -45,8 +45,7 @@
                 Build.TYPE.equals("eng") ? 1 : 0) != 0;
         final boolean hasRestriction = um.hasUserRestriction(
                 UserManager.DISALLOW_DEBUGGING_FEATURES);
-        final boolean isAdmin = um.isAdminUser();
-
-        return isAdmin && !hasRestriction && settingEnabled;
+        final boolean isAdminOrDemo = um.isAdminUser() || um.isDemoUser();
+        return isAdminOrDemo && !hasRestriction && settingEnabled;
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index d57b64f..2a86993 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -21,6 +21,7 @@
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
@@ -78,11 +79,16 @@
     @Override
     protected void updateConnectivity() {
         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
-        if (!TextUtils.isEmpty(macAddress)) {
-            mWifiMacAddress.setSummary(macAddress);
-        } else {
+        final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 0);
+        final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+
+        if (TextUtils.isEmpty(macAddress)) {
             mWifiMacAddress.setSummary(R.string.status_unavailable);
+        } else if (macRandomizationMode == 1 && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+            mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
+        } else {
+            mWifiMacAddress.setSummary(macAddress);
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
new file mode 100644
index 0000000..e2c7747
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.fuelgauge;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.provider.Settings.Secure;
+import android.util.Log;
+
+/**
+ * Utilities related to battery saver.
+ */
+public class BatterySaverUtils {
+    private static final String TAG = "BatterySaverUtils";
+
+    private BatterySaverUtils() {
+    }
+
+    private static final boolean DEBUG = false;
+
+    // Broadcast action for SystemUI to show the battery saver confirmation dialog.
+    public static final String ACTION_SHOW_START_SAVER_CONFIRMATION = "PNW.startSaverConfirmation";
+
+    /**
+     * Enable / disable battery saver by user request.
+     * - If it's the first time and needFirstTimeWarning, show the first time dialog.
+     * - If it's 4th time through 8th time, show the schedule suggestion notification.
+     *
+     * @param enable true to disable battery saver.
+     *
+     * @return true if the request succeeded.
+     */
+    public static synchronized boolean setPowerSaveMode(Context context,
+            boolean enable, boolean needFirstTimeWarning) {
+        if (DEBUG) {
+            Log.d(TAG, "Battery saver turning " + (enable ? "ON" : "OFF"));
+        }
+        final ContentResolver cr = context.getContentResolver();
+
+        if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context)) {
+            return false;
+        }
+        if (enable && !needFirstTimeWarning) {
+            setBatterySaverConfirmationAcknowledged(context);
+        }
+
+        if (context.getSystemService(PowerManager.class).setPowerSaveMode(enable)) {
+            if (enable) {
+                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
+                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1);
+
+                // TODO If enabling, and the count is between 4 and 8 (inclusive), then
+                // show the "battery saver schedule suggestion" notification.
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean maybeShowBatterySaverConfirmation(Context context) {
+        if (Secure.getInt(context.getContentResolver(),
+                Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
+            return false; // Already shown.
+        }
+        final Intent i = new Intent(ACTION_SHOW_START_SAVER_CONFIRMATION);
+        context.sendBroadcast(i);
+
+        return true;
+    }
+
+    private static void setBatterySaverConfirmationAcknowledged(Context context) {
+        Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
new file mode 100644
index 0000000..cc69e0e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.users;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.util.UserIcons;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Helper class for managing users, providing methods for removing, adding and switching users.
+ */
+public final class UserManagerHelper {
+    private static final String TAG = "UserManagerHelper";
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private OnUsersUpdateListener mUpdateListener;
+    private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mUpdateListener.onUsersUpdate();
+        }
+    };
+
+    public UserManagerHelper(Context context) {
+        mContext = context;
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+    }
+
+    /**
+     * Registers a listener for updates to all users - removing, adding users or changing user info.
+     *
+     * @param listener Instance of {@link OnUsersUpdateListener}.
+     */
+    public void registerOnUsersUpdateListener(OnUsersUpdateListener listener) {
+        mUpdateListener = listener;
+        registerReceiver();
+    }
+
+    /**
+     * Unregisters listener by unregistering {@code BroadcastReceiver}.
+     */
+    public void unregisterOnUsersUpdateListener() {
+        unregisterReceiver();
+    }
+
+    /**
+     * Gets {@link UserInfo} for the current user.
+     *
+     * @return {@link UserInfo} for the current user.
+     */
+    public UserInfo getCurrentUserInfo() {
+        return mUserManager.getUserInfo(UserHandle.myUserId());
+    }
+
+    /**
+     * Gets all the other users on the system that are not the current user.
+     *
+     * @return List of {@code UserInfo} for each user that is not the current user.
+     */
+    public List<UserInfo> getAllUsersExcludesCurrentUser() {
+        List<UserInfo> others = mUserManager.getUsers(true);
+
+        for (Iterator<UserInfo> iterator = others.iterator(); iterator.hasNext(); ) {
+            UserInfo userInfo = iterator.next();
+            if (userInfo.id == UserHandle.myUserId()) {
+                // Remove current user from the list.
+                iterator.remove();
+            }
+        }
+        return others;
+    }
+
+    // User information accessors
+
+    /**
+     * Checks whether the user is system user (admin).
+     *
+     * @param userInfo User to check against system user.
+     * @return {@code true} if system user, {@code false} otherwise.
+     */
+    public boolean userIsSystemUser(UserInfo userInfo) {
+        return userInfo.id == UserHandle.USER_SYSTEM;
+    }
+
+    /**
+     * Returns whether this user can be removed from the system.
+     *
+     * @param userInfo User to be removed
+     * @return {@code true} if they can be removed, {@code false} otherwise.
+     */
+    public boolean userCanBeRemoved(UserInfo userInfo) {
+        return !userIsSystemUser(userInfo);
+    }
+
+    /**
+     * Checks whether passed in user is the user that's currently logged in.
+     *
+     * @param userInfo User to check.
+     * @return {@code true} if current user, {@code false} otherwise.
+     */
+    public boolean userIsCurrentUser(UserInfo userInfo) {
+        return getCurrentUserInfo().id == userInfo.id;
+    }
+
+    // Current user information accessors
+
+    /**
+     * Checks if the current user is a demo user.
+     */
+    public boolean isDemoUser() {
+        return mUserManager.isDemoUser();
+    }
+
+    /**
+     * Checks if the current user is a guest user.
+     */
+    public boolean isGuestUser() {
+        return mUserManager.isGuestUser();
+    }
+
+    /**
+     * Checks if the current user is the system user (User 0).
+     */
+    public boolean isSystemUser() {
+        return mUserManager.isSystemUser();
+    }
+
+    // Current user restriction accessors
+
+    /**
+     * Return whether the current user has a restriction.
+     *
+     * @param restriction Restriction to check. Should be a UserManager.* restriction.
+     * @return Whether that restriction exists for the current user.
+     */
+    public boolean hasUserRestriction(String restriction) {
+        return mUserManager.hasUserRestriction(restriction);
+    }
+
+    /**
+     * Checks if the current user can add new users.
+     */
+    public boolean canAddUsers() {
+        return !hasUserRestriction(UserManager.DISALLOW_ADD_USER);
+    }
+
+    /**
+     * Checks if the current user can remove users.
+     */
+    public boolean canRemoveUsers() {
+        return !hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
+    }
+
+    /**
+     * Checks if the current user is allowed to switch to another user.
+     */
+    public boolean canSwitchUsers() {
+        return !hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
+    }
+
+    /**
+     * Checks if the current user can modify accounts. Demo and Guest users cannot modify accounts
+     * even if the DISALLOW_MODIFY_ACCOUNTS restriction is not applied.
+     */
+    public boolean canModifyAccounts() {
+        return !hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS) && !isDemoUser()
+                && !isGuestUser();
+    }
+
+    // User actions
+
+    /**
+     * Creates a new user on the system.
+     *
+     * @param userName Name to give to the newly created user.
+     * @return Newly created user.
+     */
+    public UserInfo createNewUser(String userName) {
+        UserInfo user = mUserManager.createUser(userName, 0 /* flags */);
+        if (user == null) {
+            // Couldn't create user, most likely because there are too many, but we haven't
+            // been able to reload the list yet.
+            Log.w(TAG, "can't create user.");
+            return null;
+        }
+        assignDefaultIcon(user);
+        return user;
+    }
+
+    /**
+     * Tries to remove the user that's passed in. System user cannot be removed.
+     * If the user to be removed is current user, it switches to the system user first, and then
+     * removes the user.
+     *
+     * @param userInfo User to be removed
+     * @return {@code true} if user is successfully removed, {@code false} otherwise.
+     */
+    public boolean removeUser(UserInfo userInfo) {
+        if (userInfo.id == UserHandle.USER_SYSTEM) {
+            Log.w(TAG, "User " + userInfo.id + " is system user, could not be removed.");
+            return false;
+        }
+
+        if (userInfo.id == getCurrentUserInfo().id) {
+            switchToUserId(UserHandle.USER_SYSTEM);
+        }
+
+        return mUserManager.removeUser(userInfo.id);
+    }
+
+    /**
+     * Switches (logs in) to another user.
+     *
+     * @param userInfo User to switch to.
+     */
+    public void switchToUser(UserInfo userInfo) {
+        if (userInfo.id == getCurrentUserInfo().id) {
+            return;
+        }
+
+        if (userInfo.isGuest()) {
+            switchToGuest(userInfo.name);
+            return;
+        }
+
+        if (UserManager.isGuestUserEphemeral()) {
+            // If switching from guest, we want to bring up the guest exit dialog instead of
+            // switching
+            UserInfo currUserInfo = getCurrentUserInfo();
+            if (currUserInfo != null && currUserInfo.isGuest()) {
+                return;
+            }
+        }
+
+        switchToUserId(userInfo.id);
+    }
+
+    /**
+     * Creates a guest session and switches into the guest session.
+     *
+     * @param guestName Username for the guest user.
+     */
+    public void switchToGuest(String guestName) {
+        UserInfo guest = mUserManager.createGuest(mContext, guestName);
+        if (guest == null) {
+            // Couldn't create user, most likely because there are too many, but we haven't
+            // been able to reload the list yet.
+            Log.w(TAG, "can't create user.");
+            return;
+        }
+        switchToUserId(guest.id);
+    }
+
+    /**
+     * Gets an icon for the user.
+     *
+     * @param userInfo User for which we want to get the icon.
+     * @return a Bitmap for the icon
+     */
+    public Bitmap getUserIcon(UserInfo userInfo) {
+        Bitmap picture = mUserManager.getUserIcon(userInfo.id);
+
+        if (picture == null) {
+            return assignDefaultIcon(userInfo);
+        }
+
+        return picture;
+    }
+
+    /**
+     * Method for scaling a Bitmap icon to a desirable size.
+     *
+     * @param icon Bitmap to scale.
+     * @param desiredSize Wanted size for the icon.
+     * @return Drawable for the icon, scaled to the new size.
+     */
+    public Drawable scaleUserIcon(Bitmap icon, int desiredSize) {
+        Bitmap scaledIcon = Bitmap.createScaledBitmap(
+                icon, desiredSize, desiredSize, true /* filter */);
+        return new BitmapDrawable(mContext.getResources(), scaledIcon);
+    }
+
+    /**
+     * Sets new Username for the user.
+     *
+     * @param user User whose name should be changed.
+     * @param name New username.
+     */
+    public void setUserName(UserInfo user, String name) {
+        mUserManager.setUserName(user.id, name);
+    }
+
+    private void registerReceiver() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_REMOVED);
+        filter.addAction(Intent.ACTION_USER_ADDED);
+        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+        mContext.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, null);
+    }
+
+    /**
+     * Assigns a default icon to a user according to the user's id.
+     *
+     * @param userInfo User to assign a default icon to.
+     * @return Bitmap that has been assigned to the user.
+     */
+    private Bitmap assignDefaultIcon(UserInfo userInfo) {
+        Bitmap bitmap = UserIcons.convertToBitmap(
+                UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
+        mUserManager.setUserIcon(userInfo.id, bitmap);
+        return bitmap;
+    }
+
+    private void switchToUserId(int id) {
+        try {
+            final ActivityManager am = (ActivityManager)
+                    mContext.getSystemService(Context.ACTIVITY_SERVICE);
+            am.switchUser(id);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't switch user.", e);
+        }
+    }
+
+    private void unregisterReceiver() {
+        mContext.unregisterReceiver(mUserChangeReceiver);
+    }
+
+    /**
+     * Interface for listeners that want to register for receiving updates to changes to the users
+     * on the system including removing and adding users, and changing user info.
+     */
+    public interface OnUsersUpdateListener {
+        /**
+         * Method that will get called when users list has been changed.
+         */
+        void onUsersUpdate();
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 68be2b4..81a2d43 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -27,6 +27,9 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.TtsSpan;
+
+import com.android.settingslib.R;
+
 import java.util.ArrayList;
 import java.util.Locale;
 
@@ -121,8 +124,7 @@
         final RelativeUnit unit;
         final int value;
         if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
-            unit = RelativeUnit.SECONDS;
-            value = seconds;
+            return context.getResources().getString(R.string.time_unit_just_now);
         } else if (seconds < 2 * SECONDS_PER_HOUR) {
             unit = RelativeUnit.MINUTES;
             value = (seconds + SECONDS_PER_MINUTE / 2)
@@ -141,7 +143,7 @@
         final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
                 ULocale.forLocale(locale),
                 null /* default NumberFormat */,
-                RelativeDateTimeFormatter.Style.SHORT,
+                RelativeDateTimeFormatter.Style.LONG,
                 android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
 
         return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index e8f5282..6e12e20 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -15,6 +15,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
 
 import java.util.List;
 
@@ -53,7 +54,7 @@
             if (connected) {
                 WifiInfo info = mWifiManager.getConnectionInfo();
                 if (info != null) {
-                    ssid = getSsid(info);
+                    ssid = getValidSsid(info);
                 } else {
                     ssid = null;
                 }
@@ -67,9 +68,9 @@
         }
     }
 
-    private String getSsid(WifiInfo info) {
+    private String getValidSsid(WifiInfo info) {
         String ssid = info.getSSID();
-        if (ssid != null) {
+        if (ssid != null && !WifiSsid.NONE.equals(ssid)) {
             return ssid;
         }
         // OK, it's not in the connectionInfo; we have to go hunting for it
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 085e112..8f80527 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -176,33 +176,34 @@
     @Deprecated
     public WifiTracker(Context context, WifiListener wifiListener,
             boolean includeSaved, boolean includeScans) {
-        this(context, new WifiListenerExecutor(wifiListener),
+        this(context, wifiListener,
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
                 newIntentFilter());
     }
 
-    // TODO(Sghuman): Clean up includeSaved and includeScans from all constructors and linked
+    // TODO(sghuman): Clean up includeSaved and includeScans from all constructors and linked
     // calling apps once IC window is complete
     public WifiTracker(Context context, WifiListener wifiListener,
             @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
-        this(context, new WifiListenerExecutor(wifiListener),
+        this(context, wifiListener,
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
                 newIntentFilter());
+
         lifecycle.addObserver(this);
     }
 
     @VisibleForTesting
-    WifiTracker(Context context, WifiListenerExecutor wifiListenerExecutor,
+    WifiTracker(Context context, WifiListener wifiListener,
             WifiManager wifiManager, ConnectivityManager connectivityManager,
             NetworkScoreManager networkScoreManager,
             IntentFilter filter) {
         mContext = context;
         mWifiManager = wifiManager;
-        mListener = wifiListenerExecutor;
+        mListener = new WifiListenerExecutor(wifiListener);
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
@@ -853,8 +854,7 @@
      *
      * <p>Also logs all callbacks invocations when verbose logging is enabled.
      */
-    @VisibleForTesting
-    public static class WifiListenerExecutor implements WifiListener {
+    @VisibleForTesting class WifiListenerExecutor implements WifiListener {
 
         private final WifiListener mDelegatee;
 
@@ -864,27 +864,29 @@
 
         @Override
         public void onWifiStateChanged(int state) {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG,
-                        String.format("Invoking onWifiStateChanged callback with state %d", state));
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onWifiStateChanged(state));
+            runAndLog(() -> mDelegatee.onWifiStateChanged(state),
+                    String.format("Invoking onWifiStateChanged callback with state %d", state));
         }
 
         @Override
         public void onConnectedChanged() {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG, "Invoking onConnectedChanged callback");
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onConnectedChanged());
+            runAndLog(mDelegatee::onConnectedChanged, "Invoking onConnectedChanged callback");
         }
 
         @Override
         public void onAccessPointsChanged() {
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG, "Invoking onAccessPointsChanged callback");
-            }
-            ThreadUtils.postOnMainThread(() -> mDelegatee.onAccessPointsChanged());
+            runAndLog(mDelegatee::onAccessPointsChanged, "Invoking onAccessPointsChanged callback");
+        }
+
+        private void runAndLog(Runnable r, String verboseLog) {
+            ThreadUtils.postOnMainThread(() -> {
+                if (mRegistered) {
+                    if (isVerboseLoggingEnabled()) {
+                        Log.i(TAG, verboseLog);
+                    }
+                    r.run();
+                }
+            });
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index fd48eea..61efabc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -76,7 +76,8 @@
      * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
      * For instance [-40,5/-30,2]
      */
-    private static String getVisibilityStatus(AccessPoint accessPoint) {
+    @VisibleForTesting
+    static String getVisibilityStatus(AccessPoint accessPoint) {
         final WifiInfo info = accessPoint.getInfo();
         StringBuilder visibility = new StringBuilder();
         StringBuilder scans24GHz = new StringBuilder();
@@ -110,6 +111,9 @@
         // TODO: sort list by RSSI or age
         long nowMs = SystemClock.elapsedRealtime();
         for (ScanResult result : accessPoint.getScanResults()) {
+            if (result == null) {
+                continue;
+            }
             if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ
                     && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) {
                 // Strictly speaking: [4915, 5825]
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
new file mode 100644
index 0000000..325ef3a
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class UserManagerHelperTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ActivityManager mActivityManager;
+    @Mock
+    private UserManagerHelper.OnUsersUpdateListener mTestListener;
+
+    private UserManagerHelper mHelper;
+    private UserInfo mCurrentUser;
+    private UserInfo mSystemUser;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
+        when(mContext.getResources())
+                .thenReturn(InstrumentationRegistry.getTargetContext().getResources());
+        mHelper = new UserManagerHelper(mContext);
+
+        mCurrentUser = createUserInfoForId(UserHandle.myUserId());
+        mSystemUser = createUserInfoForId(UserHandle.USER_SYSTEM);
+        when(mUserManager.getUserInfo(UserHandle.myUserId())).thenReturn(mCurrentUser);
+    }
+
+    @Test
+    public void testUserIsSystemUser() {
+        UserInfo testInfo = new UserInfo();
+
+        testInfo.id = UserHandle.USER_SYSTEM;
+        assertThat(mHelper.userIsSystemUser(testInfo)).isTrue();
+
+        testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id.
+        assertThat(mHelper.userIsSystemUser(testInfo)).isFalse();
+    }
+
+    @Test
+    public void testGetAllUsersExcludesCurrentUser() {
+        int currentUser = UserHandle.myUserId();
+
+        UserInfo otherUser1 = createUserInfoForId(currentUser + 1);
+        UserInfo otherUser2 = createUserInfoForId(currentUser - 1);
+        UserInfo otherUser3 = createUserInfoForId(currentUser + 2);
+
+        List<UserInfo> testUsers = new ArrayList<>();
+        testUsers.add(otherUser1);
+        testUsers.add(otherUser2);
+        testUsers.add(mCurrentUser);
+        testUsers.add(otherUser3);
+
+        when(mUserManager.getUsers(true)).thenReturn(testUsers);
+
+        // Should return 3 users that don't have currentUser id.
+        assertThat(mHelper.getAllUsersExcludesCurrentUser().size()).isEqualTo(3);
+        // Should not contain current user.
+        assertThat(mHelper.getAllUsersExcludesCurrentUser()).doesNotContain(mCurrentUser);
+        // Should contain non-current users.
+        assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser1);
+        assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser2);
+        assertThat(mHelper.getAllUsersExcludesCurrentUser()).contains(otherUser3);
+    }
+
+    @Test
+    public void testUserCanBeRemoved() {
+        UserInfo testInfo = new UserInfo();
+
+        // System user cannot be removed.
+        testInfo.id = UserHandle.USER_SYSTEM;
+        assertThat(mHelper.userCanBeRemoved(testInfo)).isFalse();
+
+        testInfo.id = UserHandle.USER_SYSTEM + 2; // Make it different than system id.
+        assertThat(mHelper.userCanBeRemoved(testInfo)).isTrue();
+    }
+
+    @Test
+    public void testUserIsCurrentUser() {
+        UserInfo testInfo = new UserInfo();
+
+        // System user cannot be removed.
+        testInfo.id = UserHandle.myUserId();
+        assertThat(mHelper.userIsCurrentUser(testInfo)).isTrue();
+
+        testInfo.id = UserHandle.myUserId() + 2;
+        assertThat(mHelper.userIsCurrentUser(testInfo)).isFalse();
+    }
+
+    @Test
+    public void testCanAddUsers() {
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(false);
+        assertThat(mHelper.canAddUsers()).isTrue();
+
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).thenReturn(true);
+        assertThat(mHelper.canAddUsers()).isFalse();
+    }
+
+    @Test
+    public void testCanRemoveUsers() {
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(false);
+        assertThat(mHelper.canRemoveUsers()).isTrue();
+
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)).thenReturn(true);
+        assertThat(mHelper.canRemoveUsers()).isFalse();
+    }
+
+    @Test
+    public void testCanSwitchUsers() {
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false);
+        assertThat(mHelper.canSwitchUsers()).isTrue();
+
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true);
+        assertThat(mHelper.canSwitchUsers()).isFalse();
+    }
+
+    @Test
+    public void testGuestCannotModifyAccounts() {
+        assertThat(mHelper.canModifyAccounts()).isTrue();
+
+        when(mUserManager.isGuestUser()).thenReturn(true);
+        assertThat(mHelper.canModifyAccounts()).isFalse();
+    }
+
+    @Test
+    public void testDemoUserCannotModifyAccounts() {
+        assertThat(mHelper.canModifyAccounts()).isTrue();
+
+        when(mUserManager.isDemoUser()).thenReturn(true);
+        assertThat(mHelper.canModifyAccounts()).isFalse();
+    }
+
+    @Test
+    public void testUserWithDisallowModifyAccountsRestrictionCannotModifyAccounts() {
+        assertThat(mHelper.canModifyAccounts()).isTrue();
+
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS))
+                .thenReturn(true);
+        assertThat(mHelper.canModifyAccounts()).isFalse();
+    }
+
+    @Test
+    public void testCreateNewUser() {
+        // Verify createUser on UserManager gets called.
+        mHelper.createNewUser("Test User");
+        verify(mUserManager).createUser("Test User", 0);
+
+        when(mUserManager.createUser("Test User", 0)).thenReturn(null);
+        assertThat(mHelper.createNewUser("Test User")).isNull();
+
+        UserInfo newUser = new UserInfo();
+        newUser.name = "Test User";
+        when(mUserManager.createUser("Test User", 0)).thenReturn(newUser);
+        assertThat(mHelper.createNewUser("Test User")).isEqualTo(newUser);
+    }
+
+    @Test
+    public void testRemoveUser() {
+        // Cannot remove system user.
+        assertThat(mHelper.removeUser(mSystemUser)).isFalse();
+
+        // Removing current user, calls "switch" to system user.
+        mHelper.removeUser(mCurrentUser);
+        verify(mActivityManager).switchUser(UserHandle.USER_SYSTEM);
+        verify(mUserManager).removeUser(mCurrentUser.id);
+
+        // Removing non-current, non-system user, simply calls removeUser.
+        UserInfo userToRemove = createUserInfoForId(mCurrentUser.id + 2);
+        mHelper.removeUser(userToRemove);
+        verify(mUserManager).removeUser(mCurrentUser.id + 2);
+    }
+
+    @Test
+    public void testSwitchToUser() {
+        // Switching to current user doesn't do anything.
+        mHelper.switchToUser(mCurrentUser);
+        verify(mActivityManager, never()).switchUser(mCurrentUser.id);
+
+        // Switching to Guest calls createGuest.
+        UserInfo guestInfo = new UserInfo(mCurrentUser.id + 1, "Test Guest", UserInfo.FLAG_GUEST);
+        mHelper.switchToUser(guestInfo);
+        verify(mUserManager).createGuest(mContext, "Test Guest");
+
+        // Switching to non-current, non-guest user, simply calls switchUser.
+        UserInfo userToSwitchTo = new UserInfo(mCurrentUser.id + 5, "Test User", 0);
+        mHelper.switchToUser(userToSwitchTo);
+        verify(mActivityManager).switchUser(mCurrentUser.id + 5);
+    }
+
+    @Test
+    public void testSwitchToGuest() {
+        mHelper.switchToGuest("Test Guest");
+        verify(mUserManager).createGuest(mContext, "Test Guest");
+
+        UserInfo guestInfo = new UserInfo(mCurrentUser.id + 2, "Test Guest", UserInfo.FLAG_GUEST);
+        when(mUserManager.createGuest(mContext, "Test Guest")).thenReturn(guestInfo);
+        mHelper.switchToGuest("Test Guest");
+        verify(mActivityManager).switchUser(mCurrentUser.id + 2);
+    }
+
+    @Test
+    public void testGetUserIcon() {
+        mHelper.getUserIcon(mCurrentUser);
+        verify(mUserManager).getUserIcon(mCurrentUser.id);
+    }
+
+    @Test
+    public void testScaleUserIcon() {
+        Bitmap fakeIcon = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        Drawable scaledIcon = mHelper.scaleUserIcon(fakeIcon, 300);
+        assertThat(scaledIcon.getIntrinsicWidth()).isEqualTo(300);
+        assertThat(scaledIcon.getIntrinsicHeight()).isEqualTo(300);
+    }
+
+    @Test
+    public void testSetUserName() {
+        UserInfo testInfo = createUserInfoForId(mCurrentUser.id + 3);
+        mHelper.setUserName(testInfo, "New Test Name");
+        verify(mUserManager).setUserName(mCurrentUser.id + 3, "New Test Name");
+    }
+
+    @Test
+    public void testRegisterUserChangeReceiver() {
+        mHelper.registerOnUsersUpdateListener(mTestListener);
+
+        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        ArgumentCaptor<UserHandle> handleCaptor = ArgumentCaptor.forClass(UserHandle.class);
+        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
+        ArgumentCaptor<String> permissionCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+
+        verify(mContext).registerReceiverAsUser(
+                receiverCaptor.capture(),
+                handleCaptor.capture(),
+                filterCaptor.capture(),
+                permissionCaptor.capture(),
+                handlerCaptor.capture());
+
+        // Verify we're listening to Intents from ALL users.
+        assertThat(handleCaptor.getValue()).isEqualTo(UserHandle.ALL);
+
+        // Verify the presence of each intent in the filter.
+        // Verify the exact number of filters. Every time a new intent is added, this test should
+        // get updated.
+        assertThat(filterCaptor.getValue().countActions()).isEqualTo(3);
+        assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_REMOVED)).isTrue();
+        assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_ADDED)).isTrue();
+        assertThat(filterCaptor.getValue().hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue();
+
+        // Verify that calling the receiver calls the listener.
+        receiverCaptor.getValue().onReceive(mContext, new Intent());
+        verify(mTestListener).onUsersUpdate();
+
+        assertThat(permissionCaptor.getValue()).isNull();
+        assertThat(handlerCaptor.getValue()).isNull();
+
+
+        // Unregister the receiver.
+        mHelper.unregisterOnUsersUpdateListener();
+        verify(mContext).unregisterReceiver(receiverCaptor.getValue());
+    }
+
+    private UserInfo createUserInfoForId(int id) {
+        UserInfo userInfo = new UserInfo();
+        userInfo.id = id;
+        return userInfo;
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 7fb4dc5..517db78 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -58,6 +58,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.settingslib.utils.ThreadUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -135,7 +137,7 @@
     @Mock private RssiCurve mockBadgeCurve1;
     @Mock private RssiCurve mockBadgeCurve2;
     @Mock private WifiManager mockWifiManager;
-    @Mock private WifiTracker.WifiListenerExecutor mockWifiListenerExecutor;
+    @Mock private WifiTracker.WifiListener mockWifiListener;
 
     private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
 
@@ -205,7 +207,7 @@
                       mAccessPointsChangedLatch.countDown();
                     }
                     return null;
-                }).when(mockWifiListenerExecutor).onAccessPointsChanged();
+                }).when(mockWifiListener).onAccessPointsChanged();
 
         // Turn on Scoring UI features
         mOriginalScoringUiSettingValue = Settings.Global.getInt(
@@ -271,7 +273,7 @@
     private WifiTracker createMockedWifiTracker() {
         final WifiTracker wifiTracker = new WifiTracker(
                 mContext,
-                mockWifiListenerExecutor,
+                mockWifiListener,
                 mockWifiManager,
                 mockConnectivityManager,
                 mockNetworkScoreManager,
@@ -690,7 +692,7 @@
         verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
 
         // mStaleAccessPoints is true
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
         assertThat(tracker.getAccessPoints().size()).isEqualTo(2);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
     }
@@ -719,7 +721,7 @@
         verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
 
         // mStaleAccessPoints is true
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         assertThat(tracker.getAccessPoints()).hasSize(1);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
@@ -762,6 +764,41 @@
     }
 
     @Test
+    public void stopTrackingShouldPreventCallbacksFromOngoingWork() throws Exception {
+        WifiTracker tracker = createMockedWifiTracker();
+        startTracking(tracker);
+
+        final CountDownLatch ready = new CountDownLatch(1);
+        final CountDownLatch latch = new CountDownLatch(1);
+        final CountDownLatch lock = new CountDownLatch(1);
+        tracker.mWorkHandler.post(() -> {
+            try {
+                ready.countDown();
+                lock.await();
+
+                tracker.mReceiver.onReceive(
+                        mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
+
+                latch.countDown();
+            } catch (InterruptedException e) {
+                fail("Interrupted Exception while awaiting lock release: " + e);
+            }
+        });
+
+        ready.await(); // Make sure we have entered the first message handler
+        tracker.onStop();
+        lock.countDown();
+        assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
+        // Wait for main thread
+        final CountDownLatch latch2 = new CountDownLatch(1);
+        ThreadUtils.postOnMainThread(latch2::countDown);
+        latch2.await();
+
+        verify(mockWifiListener, never()).onWifiStateChanged(anyInt());
+    }
+
+    @Test
     public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult()
             throws Exception {
         WifiTracker tracker = createMockedWifiTracker();
@@ -778,7 +815,7 @@
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
 
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
@@ -795,7 +832,7 @@
         tracker.mReceiver.onReceive(
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
-        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
+        verify(mockWifiListener, never()).onAccessPointsChanged();
 
         sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
@@ -816,7 +853,7 @@
     @Test
     public void onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -826,13 +863,13 @@
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
     }
 
     @Test
     public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
+        verify(mockWifiListener, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -844,7 +881,7 @@
         tracker.mReceiver.onReceive(mContext, intent);
 
         assertThat(tracker.isConnected()).isFalse();
-        verify(mockWifiListenerExecutor, times(2)).onConnectedChanged();
+        verify(mockWifiListener, times(2)).onConnectedChanged();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
new file mode 100644
index 0000000..2dbabe0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.UserIdInt;
+import android.app.ApplicationPackageManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+        ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
+        ApplicationsStateRoboTest.ShadowPackageManager.class})
+public class ApplicationsStateRoboTest {
+
+    private final static String HOME_PACKAGE_NAME = "com.android.home";
+    private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
+
+    /** Class under test */
+    private ApplicationsState mApplicationsState;
+
+    @Mock
+    private Callbacks mCallbacks;
+    @Captor
+    private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
+    @Mock
+    private StorageStatsManager mStorageStatsManager;
+
+    @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+    public static class ShadowIconDrawableFactory {
+
+        @Implementation
+        public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+            return new ColorDrawable(0);
+        }
+    }
+
+    @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+    public static class ShadowPackageManager extends
+            org.robolectric.shadows.ShadowApplicationPackageManager {
+
+        @Implementation
+        public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = new ActivityInfo();
+            resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
+            resolveInfo.activityInfo.enabled = true;
+            outActivities.add(resolveInfo);
+            return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
+        }
+
+        public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
+                @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+            List<ResolveInfo> resolveInfos = new ArrayList<>();
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = new ActivityInfo();
+            resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
+            resolveInfo.activityInfo.enabled = true;
+            resolveInfo.filter = new IntentFilter();
+            resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
+            resolveInfos.add(resolveInfo);
+            return resolveInfos;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        // Robolectric does not know about the StorageStatsManager as a system service.
+        // Registering a mock of this service as a replacement.
+        ShadowContextImpl shadowContext = Shadow.extract(
+                RuntimeEnvironment.application.getBaseContext());
+        shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+        StorageStats storageStats = new StorageStats();
+        storageStats.codeBytes = 10;
+        storageStats.dataBytes = 20;
+        storageStats.cacheBytes = 30;
+        when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
+                anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+
+        mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+        mApplicationsState.clearEntries();
+    }
+
+    private ApplicationInfo createApplicationInfo(String packageName) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.sourceDir = "foo";
+        appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+        appInfo.storageUuid = UUID.randomUUID();
+        appInfo.packageName = packageName;
+        return appInfo;
+    }
+
+    private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+        AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
+        appEntry.label = "label";
+        appEntry.mounted = true;
+        return appEntry;
+    }
+
+    private void addApp(String packageName, int id) {
+        ApplicationInfo appInfo = createApplicationInfo(packageName);
+        AppEntry appEntry = createAppEntry(appInfo, id);
+        mApplicationsState.mAppEntries.add(appEntry);
+        mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry);
+    }
+
+    private void processAllMessages() {
+        Handler mainHandler = mApplicationsState.mMainHandler;
+        Handler bkgHandler = mApplicationsState.mBackgroundHandler;
+        ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
+        ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
+        shadowBkgLooper.idle();
+        shadowMainLooper.idle();
+    }
+
+    private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
+        for (AppEntry appEntry : appEntries) {
+            if (appEntry.id == id) {
+                return appEntry;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testDefaultSessionLoadsAll() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.onResume();
+
+        addApp(HOME_PACKAGE_NAME,1);
+        addApp(LAUNCHABLE_PACKAGE_NAME,2);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(2);
+
+        for (AppEntry appEntry : appEntries) {
+            assertThat(appEntry.size).isGreaterThan(0L);
+            assertThat(appEntry.icon).isNotNull();
+        }
+
+        AppEntry homeEntry = findAppEntry(appEntries, 1);
+        assertThat(homeEntry.isHomeApp).isTrue();
+        assertThat(homeEntry.hasLauncherEntry).isFalse();
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 2);
+        assertThat(launchableEntry.hasLauncherEntry).isTrue();
+        assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsIconsOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNotNull();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsSizesOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        assertThat(launchableEntry.size).isGreaterThan(0L);
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsHomeOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+        session.onResume();
+
+        addApp(HOME_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.hasLauncherEntry).isFalse();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.isHomeApp).isTrue();
+        session.onDestroy();
+    }
+
+    @Test
+    public void testCustomSessionLoadsLeanbackOnly() {
+        Session session = mApplicationsState.newSession(mCallbacks);
+        session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+        session.onResume();
+
+        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        assertThat(appEntries.size()).isEqualTo(1);
+
+        AppEntry launchableEntry = findAppEntry(appEntries, 1);
+        assertThat(launchableEntry.icon).isNull();
+        assertThat(launchableEntry.size).isEqualTo(-1);
+        assertThat(launchableEntry.isHomeApp).isFalse();
+        assertThat(launchableEntry.hasLauncherEntry).isTrue();
+        assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+        session.onDestroy();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index d6b2006..2f5eead 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -71,6 +71,8 @@
     @Mock
     private PanProfile mPanProfile;
     @Mock
+    private HearingAidProfile mHearingAidProfile;
+    @Mock
     private BluetoothDevice mDevice1;
     @Mock
     private BluetoothDevice mDevice2;
@@ -100,6 +102,7 @@
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
+        when(mHearingAidProfile.isProfileReady()).thenReturn(true);
         mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
     }
 
@@ -280,4 +283,63 @@
         assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
         assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
     }
+
+    /**
+     * Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
+     */
+    @Test
+    public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        // Connect device1 for A2DP and HFP and device2 for Hearing Aid
+        cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+
+        // Verify that both devices are connected and none is Active
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+        // The first device is active for A2DP and HFP
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+        // The second device is active for Hearing Aid and the first device is not active
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEARING_AID);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+
+        // No active device for Hearing Aid
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 0775727..49f58a4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -56,6 +56,8 @@
     @Mock
     private PanProfile mPanProfile;
     @Mock
+    private HearingAidProfile mHearingAidProfile;
+    @Mock
     private BluetoothDevice mDevice;
     private CachedBluetoothDevice mCachedDevice;
     private Context mContext;
@@ -70,6 +72,7 @@
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
+        when(mHearingAidProfile.isProfileReady()).thenReturn(true);
         mCachedDevice = spy(
                 new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
         doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -204,6 +207,23 @@
     }
 
     @Test
+    public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+        // Test without battery level
+        // Set Hearing Aid profile to be connected and test connection state summary
+        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+        // Set device as Active for Hearing Aid and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+        // Set Hearing Aid profile to be disconnected and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
+    }
+
+    @Test
     public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
         // Test without battery level
         // Set A2DP and HFP profiles to be connected and test connection state summary
@@ -294,4 +314,24 @@
         // Verify new alias is returned on getName
         assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW);
     }
+
+    @Test
+    public void testSetActive() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
+        when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+        when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+
+        assertThat(mCachedDevice.setActive()).isFalse();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.setActive()).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a15f5fc..ccaf3fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -73,11 +73,22 @@
     }
 
     @Test
-    public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
+    public void isEnabled_settingsOn_noRestriction_notAdmin_notDemo_shouldReturnFalse() {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
         ShadowUserManager.getShadow().setIsAdminUser(false);
+        ShadowUserManager.getShadow().setIsDemoUser(false);
 
         assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
     }
+
+    @Test
+    public void isEnabled_settingsOn_noRestriction_notAdmin_isDemo_shouldReturnTrue() {
+        Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+        ShadowUserManager.getShadow().setIsAdminUser(false);
+        ShadowUserManager.getShadow().setIsDemoUser(true);
+
+        assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index d546f11..b7bc661 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.annotation.SuppressLint;
@@ -26,6 +27,7 @@
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
@@ -54,6 +56,8 @@
     @Mock
     private Preference mPreference;
 
+    private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -80,7 +84,6 @@
     public void testWifiMacAddress() {
         final WifiManager wifiManager = mock(WifiManager.class);
         final WifiInfo wifiInfo = mock(WifiInfo.class);
-        doReturn("00:11:22:33:44:55").when(wifiInfo).getMacAddress();
 
         doReturn(null).when(wifiManager).getConnectionInfo();
         doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class);
@@ -89,14 +92,21 @@
                 new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
 
         wifiMacAddressPreferenceController.displayPreference(mScreen);
-
         verify(mPreference).setSummary(R.string.status_unavailable);
 
         doReturn(wifiInfo).when(wifiManager).getConnectionInfo();
-
+        doReturn(TEST_MAC_ADDRESS).when(wifiInfo).getMacAddress();
         wifiMacAddressPreferenceController.displayPreference(mScreen);
+        verify(mPreference).setSummary(TEST_MAC_ADDRESS);
 
-        verify(mPreference).setSummary("00:11:22:33:44:55");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 1);
+        wifiMacAddressPreferenceController.displayPreference(mScreen);
+        verify(mPreference, times(2)).setSummary(TEST_MAC_ADDRESS);
+
+        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(wifiInfo).getMacAddress();
+        wifiMacAddressPreferenceController.displayPreference(mScreen);
+        verify(mPreference).setSummary(R.string.wifi_status_mac_randomized);
     }
 
     private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
new file mode 100644
index 0000000..b33df30
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.fuelgauge;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.provider.Settings.Secure;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class BatterySaverUtilsTest {
+    @Mock
+    Context mMockContext;
+
+    @Mock
+    ContentResolver mMockResolver;
+
+    @Mock
+    PowerManager mMockPowerManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
+        when(mMockContext.getSystemService(eq(PowerManager.class))).thenReturn(mMockPowerManager);
+        when(mMockPowerManager.setPowerSaveMode(anyBoolean())).thenReturn(true);
+    }
+
+    @Test
+    public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception {
+        Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
+        Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
+
+        assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+
+        verify(mMockContext, times(1)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean());
+
+        // They shouldn't have changed.
+        assertEquals(-1,
+                Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(-2,
+                Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+
+    @Test
+    public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception {
+        Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
+        Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
+
+        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+
+        verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
+
+        assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+
+    @Test
+    public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception {
+        Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
+        Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
+
+        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+
+        verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
+
+        assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(2, Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+
+    @Test
+    public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception {
+        Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
+        Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
+
+        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false));
+
+        verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
+
+        assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+
+    @Test
+    public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception {
+        Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
+        Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
+
+        // When disabling, needFirstTimeWarning doesn't matter.
+        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false));
+
+        verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
+
+        assertEquals(-1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(-2,
+                Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+
+    @Test
+    public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception {
+        Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
+        Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
+
+        // When disabling, needFirstTimeWarning doesn't matter.
+        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true));
+
+        verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
+        verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
+
+        assertEquals(-1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
+        assertEquals(-2,
+                Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a3e1bc8..bbd3a53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.testutils.shadow;
 
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.UserManager;
 
 import org.robolectric.RuntimeEnvironment;
@@ -25,6 +26,9 @@
 import org.robolectric.annotation.Resetter;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @Implements(value = UserManager.class, inheritImplementationMethods = true)
 public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
 
@@ -49,6 +53,20 @@
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
     }
 
+    @Implementation
+    public int[] getProfileIdsWithDisabled(int userId) {
+        return new int[] { 0 };
+    }
+
+    @Implementation
+    public List<UserInfo> getProfiles() {
+        UserInfo userInfo = new UserInfo();
+        userInfo.id = 0;
+        List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(userInfo);
+        return userInfos;
+    }
+
     public static ShadowUserManager getShadow() {
         return (ShadowUserManager) Shadow.extract(
                 RuntimeEnvironment.application.getSystemService(UserManager.class));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index 47dd022..532c755 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -114,7 +114,7 @@
     @Test
     public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
         final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "40 sec. ago";
+        final String expectedTime = "Just now";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -123,7 +123,7 @@
     @Test
     public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
         final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "1 min. ago";
+        final String expectedTime = "1 minute ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
                 expectedTime);
@@ -132,7 +132,7 @@
     @Test
     public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
         final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "119 sec. ago";
+        final String expectedTime = "Just now";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -141,7 +141,7 @@
     @Test
     public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
         final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "2 min. ago";
+        final String expectedTime = "2 minutes ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
                 expectedTime);
@@ -150,7 +150,7 @@
     @Test
     public void testFormatRelativeTime_TwoMinutes_withSeconds() {
         final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
-        final String expectedTime = "2 min. ago";
+        final String expectedTime = "2 minutes ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -159,7 +159,7 @@
     @Test
     public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
         final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
-        final String expectedTime = "119 min. ago";
+        final String expectedTime = "119 minutes ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -168,7 +168,7 @@
     @Test
     public void testFormatRelativeTime_TwoHours_withSeconds() {
         final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
-        final String expectedTime = "2 hr. ago";
+        final String expectedTime = "2 hours ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -177,7 +177,7 @@
     @Test
     public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
         final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
-        final String expectedTime = "47 hr. ago";
+        final String expectedTime = "47 hours ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -195,7 +195,7 @@
     @Test
     public void testFormatRelativeTime_FormatZero_WithSeconds() {
         final double testMillis = 0;
-        final String expectedTime = "0 sec. ago";
+        final String expectedTime = "Just now";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
                 expectedTime);
@@ -204,7 +204,7 @@
     @Test
     public void testFormatRelativeTime_FormatZero_NoSeconds() {
         final double testMillis = 0;
-        final String expectedTime = "0 min. ago";
+        final String expectedTime = "0 minutes ago";
 
         assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
                 expectedTime);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index ea8ecba..91d9f91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -31,6 +32,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
+import android.util.ArraySet;
 
 import com.android.settingslib.R;
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -43,6 +45,7 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
+import java.util.Set;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class WifiUtilsTest {
@@ -56,6 +59,8 @@
     private RssiCurve mockBadgeCurve;
     @Mock
     private WifiNetworkScoreCache mockWifiNetworkScoreCache;
+    @Mock
+    private AccessPoint mAccessPoint;
 
     @Before
     public void setUp() {
@@ -84,6 +89,15 @@
         assertThat(summary.contains(mContext.getString(R.string.speed_label_very_fast))).isTrue();
     }
 
+    @Test
+    public void testGetVisibilityStatus_nullResultDoesNotCrash() {
+        doReturn(null).when(mAccessPoint).getInfo();
+        Set<ScanResult> set = new ArraySet<>();
+        set.add(null);
+        doReturn(set).when(mAccessPoint).getScanResults();
+        WifiUtils.getVisibilityStatus(mAccessPoint);
+    }
+
     private static ArrayList<ScanResult> buildScanResultCache() {
         ArrayList<ScanResult> scanResults = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index ecda53a..8b78366 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -82,6 +82,10 @@
     <string name="def_wireless_charging_started_sound" translatable="false">/system/media/audio/ui/WirelessChargingStarted.ogg</string>
     <string name="def_charging_started_sound" translatable="false">/system/media/audio/ui/ChargingStarted.ogg</string>
 
+    <!-- sound trigger detection service default values -->
+    <integer name="def_max_sound_trigger_detection_service_ops_per_day" translatable="false">200</integer>
+    <integer name="def_sound_trigger_detection_service_op_timeout" translatable="false">15000</integer>
+
     <bool name="def_lockscreen_disabled">false</bool>
     <bool name="def_device_provisioned">false</bool>
     <integer name="def_dock_audio_media_enabled">1</integer>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c286881..2047f58 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -269,12 +269,6 @@
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
                 GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
         dumpSetting(s, p,
-                Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
-                GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
-        dumpSetting(s, p,
-                Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
-                GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
-        dumpSetting(s, p,
                 Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                 GlobalSettingsProto.MHL_INPUT_SWITCHING_ENABLED);
         dumpSetting(s, p,
@@ -584,6 +578,9 @@
                 Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
                 GlobalSettingsProto.BLE_SCAN_LOW_LATENCY_INTERVAL_MS);
         dumpSetting(s, p,
+                Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+                GlobalSettingsProto.BLE_SCAN_BACKGROUND_MODE);
+        dumpSetting(s, p,
                 Settings.Global.WIFI_SAVED_STATE,
                 GlobalSettingsProto.WIFI_SAVED_STATE);
         dumpSetting(s, p,
@@ -1164,12 +1161,18 @@
                 Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                 GlobalSettingsProto.CHAINED_BATTERY_ATTRIBUTION_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES,
-                GlobalSettingsProto.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
+                GlobalSettingsProto.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
         dumpSetting(s, p,
                 Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
                 GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS);
         dumpSetting(s, p,
+                Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                GlobalSettingsProto.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT);
+        dumpSetting(s, p,
+                Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                GlobalSettingsProto.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY);
+        dumpSetting(s, p,
                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                 GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
         dumpSetting(s, p,
@@ -1928,6 +1931,9 @@
         dumpSetting(s, p,
                 Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
                 SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
+        dumpSetting(s, p,
+                Settings.Secure.VOLUME_HUSH_GESTURE,
+                SecureSettingsProto.VOLUME_HUSH_GESTURE);
         // Please insert new settings using the same order as in Settings.Secure.
 
         p.end(token);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index da62d94..7fd0698 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1798,76 +1798,52 @@
         if (TextUtils.isEmpty(value)) {
             return false;
         }
-
-        final char prefix = value.charAt(0);
-        if (prefix != '+' && prefix != '-') {
-            if (forceNotify) {
-                final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
-                mSettingsRegistry.notifyForSettingsChange(key,
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
-            }
+        Setting oldSetting = getSecureSetting(
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
+        if (oldSetting == null) {
             return false;
         }
+        String oldProviders = oldSetting.getValue();
+        List<String> oldProvidersList = TextUtils.isEmpty(oldProviders)
+                ? new ArrayList<>() : new ArrayList<>(Arrays.asList(oldProviders.split(",")));
+        Set<String> newProvidersSet = new ArraySet<>();
+        newProvidersSet.addAll(oldProvidersList);
 
-        // skip prefix
-        value = value.substring(1);
-
-        Setting settingValue = getSecureSetting(
-                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
-        if (settingValue == null) {
-            return false;
-        }
-
-        String oldProviders = !settingValue.isNull() ? settingValue.getValue() : "";
-
-        int index = oldProviders.indexOf(value);
-        int end = index + value.length();
-
-        // check for commas to avoid matching on partial string
-        if (index > 0 && oldProviders.charAt(index - 1) != ',') {
-            index = -1;
-        }
-
-        // check for commas to avoid matching on partial string
-        if (end < oldProviders.length() && oldProviders.charAt(end) != ',') {
-            index = -1;
-        }
-
-        String newProviders;
-
-        if (prefix == '+' && index < 0) {
-            // append the provider to the list if not present
-            if (oldProviders.length() == 0) {
-                newProviders = value;
-            } else {
-                newProviders = oldProviders + ',' + value;
+        String[] providerUpdates = value.split(",");
+        boolean inputError = false;
+        for (String provider : providerUpdates) {
+            // do not update location_providers_allowed when input is invalid
+            if (TextUtils.isEmpty(provider)) {
+                inputError = true;
+                break;
             }
-        } else if (prefix == '-' && index >= 0) {
-            // remove the provider from the list if present
-            // remove leading or trailing comma
-            if (index > 0) {
-                index--;
-            } else if (end < oldProviders.length()) {
-                end++;
+            final char prefix = provider.charAt(0);
+            // do not update location_providers_allowed when input is invalid
+            if (prefix != '+' && prefix != '-') {
+                inputError = true;
+                break;
             }
-
-            newProviders = oldProviders.substring(0, index);
-            if (end < oldProviders.length()) {
-                newProviders += oldProviders.substring(end);
+            // skip prefix
+            provider = provider.substring(1);
+            if (prefix == '+') {
+                newProvidersSet.add(provider);
+            } else if (prefix == '-') {
+                newProvidersSet.remove(provider);
             }
-        } else {
+        }
+        String newProviders = TextUtils.join(",", newProvidersSet.toArray());
+        if (inputError == true || newProviders.equals(oldProviders)) {
             // nothing changed, so no need to update the database
             if (forceNotify) {
-                final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
-                mSettingsRegistry.notifyForSettingsChange(key,
+                mSettingsRegistry.notifyForSettingsChange(
+                        makeKey(SETTINGS_TYPE_SECURE, owningUserId),
                         Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
             }
             return false;
         }
-
         return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
-                owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
-                tag, makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
+                owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders, tag,
+                makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
     }
 
     private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -2938,7 +2914,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 158;
+            private static final int SETTINGS_VERSION = 162;
 
             private final int mUserId;
 
@@ -3636,6 +3612,82 @@
                     }
                     currentVersion = 158;
                 }
+
+                if (currentVersion == 158) {
+                    // Remove setting that specifies wifi bgscan throttling params
+                    getGlobalSettingsLocked().deleteSettingLocked(
+                        "wifi_scan_background_throttle_interval_ms");
+                    getGlobalSettingsLocked().deleteSettingLocked(
+                        "wifi_scan_background_throttle_package_whitelist");
+                    currentVersion = 159;
+                }
+
+                if (currentVersion == 159) {
+                    // Version 160: Hiding notifications from the lockscreen is only available as
+                    // primary user option, profiles can only make them redacted. If a profile was
+                    // configured to not show lockscreen notifications, ensure that at the very
+                    // least these will be come hidden.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                        Setting showNotifications = secureSettings.getSettingLocked(
+                            Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        // The default value is "1", check if user has turned it off.
+                        if ("0".equals(showNotifications.getValue())) {
+                            secureSettings.insertSettingLocked(
+                                Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, "0",
+                                null /* tag */, false /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                        // The setting is no longer valid for managed profiles, it should be
+                        // treated as if it was set to "1".
+                        secureSettings.deleteSettingLocked(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                    }
+                    currentVersion = 160;
+                }
+
+                if (currentVersion == 160) {
+                    // Version 161: Set the default value for
+                    // MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY and
+                    // SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+
+                    String oldValue = globalSettings.getSettingLocked(
+                            Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY).getValue();
+                    if (TextUtils.equals(null, oldValue)) {
+                        globalSettings.insertSettingLocked(
+                                Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                                Integer.toString(getContext().getResources().getInteger(
+                                        R.integer.def_max_sound_trigger_detection_service_ops_per_day)),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    oldValue = globalSettings.getSettingLocked(
+                            Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT).getValue();
+                    if (TextUtils.equals(null, oldValue)) {
+                        globalSettings.insertSettingLocked(
+                                Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                                Integer.toString(getContext().getResources().getInteger(
+                                        R.integer.def_sound_trigger_detection_service_op_timeout)),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 161;
+                }
+
+                if (currentVersion == 161) {
+                    // Version 161: Add a gesture for silencing phones
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting currentSetting = secureSettings.getSettingLocked(
+                            Secure.VOLUME_HUSH_GESTURE);
+                    if (currentSetting.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.VOLUME_HUSH_GESTURE,
+                                Integer.toString(Secure.VOLUME_HUSH_VIBRATE),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    currentVersion = 162;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6d341c5..449946d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -42,6 +42,7 @@
 import android.util.Base64;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
@@ -386,6 +387,9 @@
             mSettings.put(name, newState);
         }
 
+        StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
+            makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+
         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
 
         updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
@@ -410,6 +414,10 @@
 
         Setting oldState = mSettings.remove(name);
 
+        StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
+            oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+            StatsLog.SETTING_CHANGED__REASON__DELETED);
+
         updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
                 null, oldState.defaultValue, null);
 
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index e2a8fba..b6f51bc 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -17,8 +17,8 @@
 package com.android.providers.settings;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertSame;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
 import static junit.framework.Assert.fail;
 
 import android.content.ContentResolver;
@@ -32,9 +32,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
-import org.junit.Test;
-
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.Test;
 
 /**
  * Tests for the SettingContentProvider.
@@ -688,4 +687,112 @@
             cursor.close();
         }
     }
+
+    @Test
+    public void testUpdateLocationProvidersAllowedLocked_enableProviders() throws Exception {
+        setSettingViaFrontEndApiAndAssertSuccessfulChange(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_MODE,
+                String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
+                UserHandle.USER_SYSTEM);
+
+        // Enable one provider
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps");
+
+        assertEquals(
+                "Wrong location providers",
+                "gps",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Enable a list of providers, including the one that is already enabled
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "+gps,+network,+network");
+
+        assertEquals(
+                "Wrong location providers",
+                "gps,network",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+    }
+
+    @Test
+    public void testUpdateLocationProvidersAllowedLocked_disableProviders() throws Exception {
+        setSettingViaFrontEndApiAndAssertSuccessfulChange(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_MODE,
+                String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY),
+                UserHandle.USER_SYSTEM);
+
+        // Disable providers that were enabled
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "-gps,-network");
+
+        assertEquals(
+                "Wrong location providers",
+                "",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Disable a provider that was not enabled
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "-test");
+
+        assertEquals(
+                "Wrong location providers",
+                "",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+    }
+
+    @Test
+    public void testUpdateLocationProvidersAllowedLocked_enableAndDisable() throws Exception {
+        setSettingViaFrontEndApiAndAssertSuccessfulChange(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_MODE,
+                String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
+                UserHandle.USER_SYSTEM);
+
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "+gps,+network,+test");
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test");
+
+        assertEquals(
+                "Wrong location providers",
+                "gps,network",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+    }
+
+    @Test
+    public void testUpdateLocationProvidersAllowedLocked_invalidInput() throws Exception {
+        setSettingViaFrontEndApiAndAssertSuccessfulChange(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_MODE,
+                String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
+                UserHandle.USER_SYSTEM);
+
+        // update providers with a invalid string
+        updateStringViaProviderApiSetting(
+                SETTING_TYPE_SECURE,
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "+gps, invalid-string");
+
+        // Verifies providers list does not change
+        assertEquals(
+                "Wrong location providers",
+                "",
+                queryStringViaProviderApi(
+                        SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+    }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 589ae2a..b49f1ac 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -121,6 +121,7 @@
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index f65efb8..68293d9 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -37,6 +37,7 @@
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     SystemUIPluginLib \
     SystemUISharedLib \
+    android-support-car \
     android-support-v4 \
     android-support-v7-recyclerview \
     android-support-v7-preference \
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 02d0d70..8253083 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -44,6 +44,15 @@
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_pin_password_code">Type PIN to unlock</string>
 
+    <!-- Instructions telling the user to enter their PIN password to unlock the keyguard [CHAR LIMIT=30] -->
+    <string name="keyguard_enter_your_pin">Enter your PIN</string>
+
+    <!-- Instructions telling the user to enter their pattern to unlock the keyguard [CHAR LIMIT=30] -->
+    <string name="keyguard_enter_your_pattern">Enter your Pattern</string>
+
+    <!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
+    <string name="keyguard_enter_your_password">Enter your Password</string>
+
     <!-- Instructions telling the user that they entered the wrong pin while trying
          to unlock the keyguard.  Displayed in one line in a large font.  -->
     <string name="keyguard_password_wrong_pin_code">Incorrect PIN code.</string>
diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
new file mode 100644
index 0000000..25b449a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners
+        android:bottomLeftRadius="@dimen/car_radius_3"
+        android:topLeftRadius="0dp"
+        android:bottomRightRadius="@dimen/car_radius_3"
+        android:topRightRadius="0dp"
+        />
+</shape>
diff --git a/packages/SystemUI/res/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml
deleted file mode 100644
index 59000fc..0000000
--- a/packages/SystemUI/res/drawable/heads_up_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ 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
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="-90"
-            android:startColor="#55000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
index 382d9d5..726d814 100644
--- a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
@@ -14,16 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="14dp"
+        android:height="24dp"
+        android:viewportWidth="14"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M4,7h4v10H6V9H4V7z M15.83,11.72L18.66,7h-2.33l-1.66,2.77L13,7h-2.33l2.83,4.72L10.33,17h2.33l2-3.34l2,3.34H19 L15.83,11.72z" />
+        android:pathData="M5.62,16.29H4.58V9.06l-1.79,0.8V8.88l2.67-1.17h0.16V16.29z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M11.08,11.02l1.61-3.27h1.26l-2.22,4.23l2.27,4.3h-1.27l-1.64-3.33l-1.65,3.33H8.16l2.28-4.3L8.21,7.75h1.25L11.08,11.02z" />
     <path
-        android:pathData="M0,0h24v24H0V0z" />
-</vector>
+        android:pathData="M 0 0 H 13.99 V 24 H 0 V 0 Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
index ce003e4..7a539ff 100644
--- a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
@@ -14,16 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="14dp"
+        android:height="24dp"
+        android:viewportWidth="14"
+        android:viewportHeight="24">
 
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M3.83,11.38h0.66c0.43,0,0.75-0.13,0.98-0.39s0.35-0.62,0.35-1.07c0-1-0.39-1.5-1.16-1.5c-0.37,0-0.66,0.13-0.87,0.4 S3.47,9.44,3.47,9.88H2.44c0-0.68,0.21-1.25,0.62-1.69s0.95-0.67,1.6-0.67c0.67,0,1.21,0.21,1.6,0.63s0.59,1.01,0.59,1.78 c0,0.39-0.1,0.76-0.31,1.1s-0.47,0.59-0.8,0.75c0.8,0.3,1.21,0.96,1.21,2c0,0.76-0.21,1.37-0.64,1.82s-0.98,0.68-1.66,0.68 c-0.68,0-1.22-0.21-1.64-0.64s-0.62-1-0.62-1.73h1.04c0,0.45,0.11,0.81,0.33,1.08s0.52,0.4,0.9,0.4c0.39,0,0.69-0.13,0.92-0.39 s0.34-0.66,0.34-1.2c0-1.04-0.49-1.55-1.47-1.55H3.83V11.38z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M3,7v2h5v2H4v2h4v2H3v2h5c1.1,0,2-0.9,2-2v-1.5c0-0.83-0.67-1.5-1.5-1.5c0.83,0,1.5-0.67,1.5-1.5V9c0-1.1-0.9-2-2-2H3z M21,11v4c0,1.1-0.9,2-2,2h-5c-1.1,0-2-0.9-2-2V9c0-1.1,0.9-2,2-2h5c1.1,0,2,0.9,2,2h-2h-5v6h5v-2h-2.5v-2H21z" />
-</vector>
+        android:pathData="M14,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.15-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92H14V15.11z" />
+    <path
+        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
index 8e22e06..b2fab0c8 100644
--- a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
@@ -14,16 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="14dp"
+        android:height="24dp"
+        android:viewportWidth="14"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M9,7H7v5H5V7H3v7h4v3h2v-3h2v-2H9V7z M17,11v2h2v2h-5V9h7c0-1.1-0.9-2-2-2h-5c-1.1,0-2,0.9-2,2v6c0,1.1,0.9,2,2,2h5 c1.1,0,2-0.9,2-2v-4H17z" />
+        android:pathData="M6.42,13.3h0.95v0.88H6.42v1.98H5.38v-1.98H2.16v-0.64l3.17-5.91h1.09C6.42,7.63,6.42,13.3,6.42,13.3z M3.31,13.3h2.07 V9.25L3.31,13.3z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M13.99,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.62V15.11z" />
     <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
index 32add0c..bdbb2df3 100644
--- a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
@@ -14,16 +14,20 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="20dp"
+        android:height="24dp"
+        android:viewportWidth="20"
+        android:viewportHeight="24">
 
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.18,13.3h0.95v0.88H6.18v1.98H5.14v-1.98H1.92v-0.64l3.17-5.91h1.09V13.3z M3.07,13.3h2.07V9.25L3.07,13.3z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M13,11v2h2v2h-4V9h6c0-1.1-0.9-2-2-2h-4C9.9,7,9,7.9,9,9v6c0,1.1,0.9,2,2,2h4c1.1,0,2-0.9,2-2v-4H13z M24,11h-2V9h-2v2h-2v2 h2v2h2v-2h2V11z M7,7H5v5H3V7H1v7h4v3h2v-3h1v-2H7V7z" />
+        android:pathData="M13.75,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.63V15.11z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 20 9.64 L 18 9.64 L 18 7.64 L 17 7.64 L 17 9.64 L 15 9.64 L 15 10.64 L 17 10.64 L 17 12.64 L 18 12.64 L 18 10.64 L 20 10.64 Z" />
+    <path
+        android:pathData="M 0 0 H 20 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
index 80e507b..1a4a2e3 100644
--- a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="7dp"
+        android:height="24dp"
+        android:viewportWidth="7"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M 16 9 L 16 7 L 8 7 L 8 17 L 16 17 L 16 15 L 10 15 L 10 13 L 16 13 L 16 11 L 10 11 L 10 9 Z" />
+        android:pathData="M6.5,12.32H3.48v3.02H7v0.92H2.41V7.73h4.53v0.92H3.48v2.75H6.5V12.32z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:pathData="M 0 0 H 7 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
index 04049ce..d6a0488 100644
--- a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="8dp"
+        android:height="24dp"
+        android:viewportWidth="8"
+        android:viewportHeight="24">
 
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
-    <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12,11v2h2v2H9V9h0.44H14h2c0-1.1-0.9-2-2-2H9C7.9,7,7,7.9,7,9v6c0,1.1,0.9,2,2,2h5c1.1,0,2-0.9,2-2v-4H12z" />
+        android:pathData="M8,15.21l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.2 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68S7.95,9.39,8,10.23H6.93C6.88,9.7,6.74,9.28,6.49,8.99 S5.85,8.54,5.34,8.54c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69 c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96H5.37v-0.92H8V15.21z" />
+    <path
+        android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
index 31cc4a7..be85bbb 100644
--- a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="8dp"
+        android:height="24dp"
+        android:viewportWidth="8"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M15,11H9V7H7v10h2v-4h6v4h2V7h-2V11z" />
+        android:pathData="M8,16.27H6.92v-3.94H3.49v3.94H2.42V7.73h1.07v3.67h3.43V7.73H8V16.27z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
index ca1020e..f31f83c 100644
--- a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
@@ -14,19 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="14dp"
+        android:height="24dp"
+        android:viewportWidth="14"
+        android:viewportHeight="24">
 
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M7.64,16.27H6.56v-3.94H3.13v3.94H2.06V7.73h1.07v3.67h3.43V7.73h1.08V16.27z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12,11H6V7H4v10h2v-4h6v4h2V7h-2V11z" />
+        android:pathData="M 14 9.73 L 12 9.73 L 12 7.73 L 11 7.73 L 11 9.73 L 9 9.73 L 9 10.73 L 11 10.73 L 11 12.73 L 12 12.73 L 12 10.73 L 14 10.73 Z" />
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M22,11h-2V9h-2v2h-2v2h2v2h2v-2h2V11z" />
+        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
index 3345f61..6a7f18b 100644
--- a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
+++ b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:pathData="M20.000000,5.000000L4.000000,5.000000C2.900000,5.000000 2.000000,5.900000 2.000000,7.000000l0.000000,10.000000c0.000000,1.100000 0.900000,2.000000 2.000000,2.000000l16.000000,0.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L22.000000,7.000000C22.000000,5.900000 21.100000,5.000000 20.000000,5.000000zM11.000000,8.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,8.000000zM11.000000,11.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,11.000000zM8.000000,8.000000l2.000000,0.000000l0.000000,2.000000L8.000000,10.000000L8.000000,8.000000zM8.000000,11.000000l2.000000,0.000000l0.000000,2.000000L8.000000,13.000000L8.000000,11.000000zM7.000000,13.000000L5.000000,13.000000l0.000000,-2.000000l2.000000,0.000000L7.000000,13.000000zM7.000000,10.000000L5.000000,10.000000L5.000000,8.000000l2.000000,0.000000L7.000000,10.000000zM16.000000,17.000000L8.000000,17.000000l0.000000,-2.000000l8.000000,0.000000L16.000000,17.000000zM16.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L16.000000,13.000000zM16.000000,10.000000l-2.000000,0.000000L14.000000,8.000000l2.000000,0.000000L16.000000,10.000000zM19.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L19.000000,13.000000zM19.000000,10.000000l-2.000000,0.000000L17.000000,8.000000l2.000000,0.000000L19.000000,10.000000z"
+        android:pathData="M21,4H3C1.9,4 1,4.9 1,6v13c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V6C23,4.9 22.1,4 21,4zM21,19H3V6h18V19zM9,8h2v2H9V8zM5,8h2v2H5V8zM8,16h8v1H8V16zM13,8h2v2h-2V8zM9,12h2v2H9V12zM5,12h2v2H5V12zM13,12h2v2h-2V12zM17,8h2v2h-2V8zM17,12h2v2h-2V12z"
         android:fillColor="?attr/singleToneColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
index b517fc8..65b9813 100644
--- a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
+++ b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
@@ -20,6 +20,7 @@
         android:viewportHeight="24.0">
 
     <path
-        android:fillColor="#757575"
+        android:fillColor="#000000"
+        android:alpha="0.87"
         android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
index 5d90965..e45b5e0 100644
--- a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
@@ -14,16 +14,20 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="18dp"
+        android:height="24dp"
+        android:viewportWidth="18"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M6,14h3v2H4V8h2V14z M9,10h2v6h2v-6h2V8H9V10z M21,10V8h-5v8h2h3v-2h-3v-0.67V13h3v-2h-3v-1H21z" />
+        android:pathData="M3.79,15.35h3.35v0.92H2.71V7.73h1.08V15.35z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.15,8.65H9.91v7.61H8.84V8.65H6.6V7.73h5.55V8.65z" />
     <path
-        android:pathData="M0,0h24v24H0V0z" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17.5,12.32h-3.02v3.02H18v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+    <path
+        android:pathData="M 0 0 H 18 V 24 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
index 0366e24..553a5bd 100644
--- a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
@@ -14,16 +14,23 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M3,14h3v2H1V8h2V14z M5,10h2v6h2v-6h2V8H5V10z M12,16h5v-2h-3v-1h3v-2h-3v-1h3V8h-5V16z M24,11h-2V9h-2v2h-2v2h2v2h2v-2h2 V11z" />
+        android:pathData="M3.91,15.35h3.35v0.92H2.84V7.73h1.08V15.35z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.28,8.65h-2.24v7.61H8.96V8.65H6.73V7.73h5.55V8.65z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17.63,12.32h-3.02v3.02h3.52v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 24 9.76 L 22 9.76 L 22 7.76 L 21 7.76 L 21 9.76 L 19 9.76 L 19 10.76 L 21 10.76 L 21 12.76 L 22 12.76 L 22 10.76 L 24 10.76 Z" />
     <path
         android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
index 4baa472..bd2edf3 100644
--- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -14,11 +14,15 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8.5dp"
-        android:height="17dp"
-        android:viewportWidth="6.0"
-        android:viewportHeight="12.0">
+        android:width="@dimen/signal_icon_size"
+        android:height="@dimen/signal_icon_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
-</vector>
+        android:pathData="M7.8,7.2L9,10H7L5.87,7.33H4V10H2V2h5c1.13,0,2,0.87,2,2v1.33C9,6.13,8.47,6.87,7.8,7.2z M7,4H4v1.33h3V4z" />
+    <path
+        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+    <path
+        android:pathData="M0,0h24v24H0V0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
new file mode 100644
index 0000000..dca50a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -0,0 +1,68 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="@dimen/car_margin"
+    android:layout_marginEnd="@dimen/car_margin"
+    android:background="@drawable/car_rounded_bg_bottom"
+    android:theme="@style/qs_theme"
+    android:clipChildren="false" >
+    <LinearLayout
+        android:id="@+id/volume_dialog"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal|top"
+        android:orientation="vertical"
+        android:clipChildren="false" >
+
+        <LinearLayout
+            android:id="@+id/main"
+            android:layout_width="match_parent"
+            android:minWidth="@dimen/volume_dialog_panel_width"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:elevation="@dimen/volume_panel_elevation" >
+            <LinearLayout
+                android:id="@+id/car_volume_dialog_rows"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:orientation="vertical" >
+                <!-- volume rows added and removed here! :-) -->
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/car_single_line_list_item_height"
+        android:gravity="center"
+        android:layout_marginEnd="@dimen/car_keyline_1">
+        <ImageButton
+            android:id="@+id/expand"
+            android:layout_gravity="center"
+            android:layout_width="@dimen/car_primary_icon_size"
+            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_marginEnd="@dimen/car_keyline_1"
+            android:src="@drawable/car_ic_arrow_drop_up"
+            android:tint="@color/car_tint"
+            android:scaleType="fitCenter"
+        />
+    </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/car_volume_dialog_row.xml b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
new file mode 100644
index 0000000..14baf49
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
@@ -0,0 +1,47 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tag="row"
+    android:layout_height="@dimen/car_single_line_list_item_height"
+    android:layout_width="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:gravity="center"
+        android:layout_gravity="center"
+        android:orientation="horizontal" >
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/volume_row_icon"
+            android:layout_width="@dimen/car_primary_icon_size"
+            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_marginStart="@dimen/car_keyline_1"
+            android:tint="@color/car_tint"
+            android:scaleType="fitCenter"
+            android:soundEffectsEnabled="false" />
+        <SeekBar
+                android:id="@+id/volume_row_slider"
+                android:clickable="true"
+                android:layout_marginStart="@dimen/car_keyline_3"
+                android:layout_marginEnd="@dimen/car_keyline_3"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/car_single_line_list_item_height"/>
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
new file mode 100644
index 0000000..bacb5c1
--- /dev/null
+++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
@@ -0,0 +1,48 @@
+<?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
+  -->
+<com.android.systemui.statusbar.HeadsUpStatusBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:visibility="invisible"
+    android:id="@+id/heads_up_status_bar_view"
+    android:alpha="0"
+>
+    <!-- This is a space just used as a layout and it's not actually displaying anything. We're
+         repositioning the statusbar icon to the position where this is laid out when showing this
+         view. -->
+    <Space
+        android:id="@+id/icon_placeholder"
+        android:layout_width="@dimen/status_bar_icon_drawing_size"
+        android:layout_height="@dimen/status_bar_icon_drawing_size"
+        android:layout_gravity="center_vertical"
+    />
+    <TextView
+        android:id="@+id/text"
+        android:textAppearance="@style/TextAppearance.HeadsUpStatusBarText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal"
+        android:textAlignment="viewStart"
+        android:paddingStart="6dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+    />
+
+</com.android.systemui.statusbar.HeadsUpStatusBarView>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 2e92042..cc6e3bf 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,34 +50,28 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:layout_gravity="center_vertical"
+        android:paddingEnd="1dp"
         android:visibility="gone" />
+    <Space
+        android:id="@+id/mobile_roaming_space"
+        android:layout_height="match_parent"
+        android:layout_width="@dimen/roaming_icon_start_padding"
+        android:visibility="gone"
+    />
     <FrameLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical">
         <com.android.systemui.statusbar.AnimatedImageView
-            android:theme="@style/DualToneLightTheme"
             android:id="@+id/mobile_signal"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             systemui:hasOverlappingRendering="false"
             />
-        <com.android.systemui.statusbar.AnimatedImageView
-            android:theme="@style/DualToneDarkTheme"
-            android:id="@+id/mobile_signal_dark"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:alpha="0.0"
-            systemui:hasOverlappingRendering="false"
-            />
         <ImageView
             android:id="@+id/mobile_roaming"
             android:layout_width="wrap_content"
-            android:layout_height="17dp"
-            android:paddingStart="22dp"
-            android:paddingTop="1.5dp"
-            android:paddingBottom="3dp"
-            android:scaleType="fitCenter"
+            android:layout_height="wrap_content"
             android:src="@drawable/stat_sys_roaming"
             android:contentDescription="@string/data_connection_roaming"
             android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml
index 6732e6c..fa696cc 100644
--- a/packages/SystemUI/res/layout/notification_icon_area.xml
+++ b/packages/SystemUI/res/layout/notification_icon_area.xml
@@ -18,12 +18,14 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_icon_area_inner"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="match_parent"
+    android:clipChildren="false">
     <com.android.systemui.statusbar.phone.NotificationIconContainer
         android:id="@+id/notificationIcons"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:gravity="center_vertical"
-        android:orientation="horizontal"/>
+        android:orientation="horizontal"
+        android:clipChildren="false"/>
 </com.android.keyguard.AlphaOptimizedLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 7500bc6..ea1ad2d1 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -60,7 +60,7 @@
             android:layout_marginEnd="32dp"
             android:gravity="center_vertical|start"
             android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAppearance="@style/TextAppearance.QS.TileLabel"
             android:textColor="?android:attr/textColorPrimary"
             android:textDirection="locale"
             android:singleLine="true" />
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index 734c877..26cf792 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -14,7 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
-<FrameLayout
+<com.android.systemui.RegionInterceptingFrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -32,4 +32,4 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded" />
-</FrameLayout>
+</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8c0b9ba..9d336e2 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -3,16 +3,16 @@
 **
 ** Copyright 2006, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     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 
+** 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.
 */
 -->
@@ -34,7 +34,7 @@
         android:id="@+id/notification_lights_out"
         android:layout_width="@dimen/status_bar_icon_size"
         android:layout_height="match_parent"
-        android:paddingStart="6dip"
+        android:paddingStart="@dimen/status_bar_padding_start"
         android:paddingBottom="2dip"
         android:src="@drawable/ic_sysbar_lights_out_dot_small"
         android:scaleType="center"
@@ -44,7 +44,7 @@
     <LinearLayout android:id="@+id/status_bar_contents"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingStart="6dp"
+        android:paddingStart="@dimen/status_bar_padding_start"
         android:paddingEnd="8dp"
         android:orientation="horizontal"
         >
@@ -53,33 +53,41 @@
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout="@layout/operator_name" />
-
-        <LinearLayout
+        <FrameLayout
             android:layout_height="match_parent"
             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"
-            />
+            android:layout_weight="1">
 
-            <!-- 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" />
+            <include layout="@layout/heads_up_status_bar_layout" />
 
-        </LinearLayout>
+            <LinearLayout
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"
+                android:clipChildren="false"
+            >
+                <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"
+                    android:clipChildren="false"/>
+
+            </LinearLayout>
+        </FrameLayout>
 
         <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
         <android.widget.Space
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ef442e5..75403b9 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -51,14 +51,6 @@
         sysui:ignoreRightInset="true"
         />
 
-    <com.android.systemui.statusbar.AlphaOptimizedView
-        android:id="@+id/heads_up_scrim"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/heads_up_scrim_height"
-        android:background="@drawable/heads_up_scrim"
-        sysui:ignoreRightInset="true"
-        android:importantForAccessibility="no"/>
-
     <FrameLayout
         android:id="@+id/status_bar_container"
         android:layout_width="match_parent"
diff --git a/wifi/java/android/net/wifi/ScanSettings.aidl b/packages/SystemUI/res/values-sw600dp/bools.xml
similarity index 64%
rename from wifi/java/android/net/wifi/ScanSettings.aidl
rename to packages/SystemUI/res/values-sw600dp/bools.xml
index ebd2a39..05e38bd 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/packages/SystemUI/res/values-sw600dp/bools.xml
@@ -1,5 +1,6 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,8 +13,9 @@
  * 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;
-
-parcelable ScanSettings;
+*/
+-->
+<resources>
+    <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+    <bool name="qs_show_user_switcher_for_single_user">true</bool>
+</resources>
diff --git a/wifi/java/android/net/wifi/ScanSettings.aidl b/packages/SystemUI/res/values/bools.xml
similarity index 64%
copy from wifi/java/android/net/wifi/ScanSettings.aidl
copy to packages/SystemUI/res/values/bools.xml
index ebd2a39..499e24e 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/packages/SystemUI/res/values/bools.xml
@@ -1,5 +1,6 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,8 +13,9 @@
  * 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;
-
-parcelable ScanSettings;
+*/
+-->
+<resources>
+    <!-- Whether to show the user switcher in quick settings when only a single user is present. -->
+    <bool name="qs_show_user_switcher_for_single_user">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5b038b1..906ca4a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -79,9 +79,6 @@
     <!-- The color of the material notification background when dark -->
     <color name="notification_material_background_dark_color">#ff333333</color>
 
-    <!-- The color of the material notification background when low priority -->
-    <color name="notification_material_background_low_priority_color">#fff5f5f5</color>
-
     <!-- The color of the dividing line between grouped notifications. -->
     <color name="notification_divider_color">#FF616161</color>
 
@@ -91,9 +88,6 @@
     <!-- The color of the ripples on the untinted notifications -->
     <color name="notification_ripple_untinted_color">#28000000</color>
 
-    <!-- The color of the ripples on the low priority notifications -->
-    <color name="notification_ripple_color_low_priority">#30000000</color>
-
     <!-- The color of the ripples on the tinted notifications -->
     <color name="notification_ripple_tinted_color">#30ffffff</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e30b86a..d65e42b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -110,6 +110,12 @@
     <!-- Side padding on the lockscreen on the side of notifications -->
     <dimen name="notification_side_paddings">4dp</dimen>
 
+    <!-- padding between the heads up and the statusbar -->
+    <dimen name="heads_up_status_bar_padding">8dp</dimen>
+
+    <!-- heads up elevation that is added if the view is pinned -->
+    <dimen name="heads_up_pinned_elevation">16dp</dimen>
+
     <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
          and the notification won't use this much, but is measured with wrap_content -->
     <dimen name="notification_messaging_actions_min_height">196dp</dimen>
@@ -451,7 +457,6 @@
     <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
     <!-- Minimum margin between clock and status bar -->
     <dimen name="keyguard_clock_top_margin">36dp</dimen>
-    <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
 
@@ -637,6 +642,10 @@
          type icon is wide. -->
     <dimen name="wide_type_icon_start_padding">2dp</dimen>
 
+    <!-- Padding between the mobile signal indicator and the start icon when the roaming icon
+         is displayed in the upper left corner. -->
+    <dimen name="roaming_icon_start_padding">2dp</dimen>
+
     <!-- Extra padding between multiple phone signal icons. -->
     <dimen name="secondary_telephony_padding">2dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bf3fa29..a4b7608 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -365,9 +365,6 @@
     <!-- Content description of the data connection type GPRS. [CHAR LIMIT=NONE] -->
     <string name="data_connection_gprs">GPRS</string>
 
-    <!-- Content description of the data connection type 1x. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_1x">1 X</string>
-
     <!-- Content description of the data connection type HSPA and its variants. [CHAR LIMIT=NONE] -->
     <string name="data_connection_hspa">HSPA</string>
 
@@ -672,7 +669,7 @@
     <string name="ethernet_label">Ethernet</string>
 
     <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_header_onboarding_text">Press &amp; hold on the icons for more options</string>
+    <string name="quick_settings_header_onboarding_text">Touch &amp; hold icons for more options</string>
     <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
     <string name="quick_settings_dnd_label">Do not disturb</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
@@ -1356,8 +1353,6 @@
 
     <string name="volume_dialog_title">%s volume controls</string>
 
-    <string name="volume_dialog_ringer_guidance_vibrate">Calls and notifications will vibrate</string>
-    <string name="volume_dialog_ringer_guidance_silent">Calls and notifications will be muted</string>
     <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring</string>
 
     <string name="output_title">Media output</string>
@@ -1848,11 +1843,14 @@
     <string name="right_icon">Right icon</string>
 
     <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
-    <string name="drag_to_add_tiles">Drag to add tiles</string>
+    <string name="drag_to_add_tiles">Hold and drag to add tiles</string>
 
     <!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] -->
     <string name="drag_to_remove_tiles">Drag here to remove</string>
 
+    <!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] -->
+    <string name="drag_to_remove_disabled">You need at least 6 tiles</string>
+
     <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
     <string name="qs_edit">Edit</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1470dfa..20d8834 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -490,6 +490,10 @@
         <item name="android:paddingEnd">8dp</item>
     </style>
 
+    <style name="TextAppearance.HeadsUpStatusBarText"
+           parent="@*android:style/TextAppearance.Material.Notification.Info">
+    </style>
+
     <style name="edit_theme" parent="qs_base">
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
index 85d362a..c227fee 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
@@ -49,6 +49,10 @@
                     mHandler.post(mComposeTask);
                 }
                 List<AppTransitionAnimationSpecCompat> specs = mComposeTask.get();
+                // Clear reference to the compose task this future holds onto the reference to it's
+                // implementation (which can leak references to the bitmap it creates for the
+                // transition)
+                mComposeTask = null;
                 if (specs == null) {
                     return null;
                 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 940c9ef..5fa6c79 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -29,6 +29,8 @@
 
     private IRecentsAnimationController mAnimationController;
 
+    public RecentsAnimationControllerCompat() { }
+
     public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
         mAnimationController = animationController;
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 2de3ae2..9355acf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -23,7 +23,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.RemoteAnimationAdapter;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -124,4 +123,12 @@
             Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e);
         }
     }
+
+    public void setShelfHeight(boolean visible, int shelfHeight) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().setShelfHeight(visible, shelfHeight);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to set shelf height");
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6098e4e..d24675c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -68,6 +68,7 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
@@ -78,7 +79,6 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import com.google.android.collect.Lists;
@@ -985,6 +985,15 @@
         }
 
         /**
+         * Determine whether the device is plugged in (USB, power).
+         * @return true if the device is plugged in wired (as opposed to wireless)
+         */
+        public boolean isPluggedInWired() {
+            return plugged == BatteryManager.BATTERY_PLUGGED_AC
+                    || plugged == BatteryManager.BATTERY_PLUGGED_USB;
+        }
+
+        /**
          * Whether or not the device is charged. Note that some devices never return 100% for
          * battery level, so this allows either battery level or status to determine if the
          * battery is charged.
@@ -1717,6 +1726,7 @@
         callback.onPhoneStateChanged(mPhoneState);
         callback.onRefreshCarrierInfo();
         callback.onClockVisibilityChanged();
+        callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
             callback.onSimStateChanged(state.subId, state.slotId, state.simState);
diff --git a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
new file mode 100644
index 0000000..646f69e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.Context;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.widget.FrameLayout;
+
+/**
+ * Frame layout that will intercept the touches of children if they want to
+ */
+public class RegionInterceptingFrameLayout extends FrameLayout {
+    public RegionInterceptingFrameLayout(Context context) {
+        super(context);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> {
+        internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        internalInsetsInfo.touchableRegion.setEmpty();
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (!(child instanceof RegionInterceptableView)) {
+                continue;
+            }
+            RegionInterceptableView riv = (RegionInterceptableView) child;
+            if (!riv.shouldInterceptTouch()) {
+                continue;
+            }
+            Region unionRegion = riv.getInterceptRegion();
+            if (unionRegion == null) {
+                continue;
+            }
+
+            internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION);
+        }
+    };
+
+    public interface RegionInterceptableView {
+        default public boolean shouldInterceptTouch() {
+            return false;
+        }
+
+        public Region getInterceptRegion();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9a20c81..a0fa69e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -49,6 +49,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
@@ -232,8 +233,7 @@
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                         | WindowManager.LayoutParams.FLAG_SLIPPERY
@@ -317,7 +317,8 @@
         }
     }
 
-    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener {
+    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
+            RegionInterceptableView {
 
         private final DisplayInfo mInfo = new DisplayInfo();
         private final Paint mPaint = new Paint();
@@ -468,5 +469,19 @@
                 }
             }
         }
+
+        @Override
+        public boolean shouldInterceptTouch() {
+            return mInfo.displayCutout != null && getVisibility() == VISIBLE;
+        }
+
+        @Override
+        public Region getInterceptRegion() {
+            if (mInfo.displayCutout == null) {
+                return null;
+            }
+
+            return mInfo.displayCutout.getBounds();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 6b30a89..30fbef6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -51,10 +51,13 @@
         }
     }
 
-    public static void overrideNotificationAppName(Context context, Notification.Builder n) {
+    public static void overrideNotificationAppName(Context context, Notification.Builder n,
+            boolean system) {
         final Bundle extras = new Bundle();
-        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                context.getString(com.android.internal.R.string.android_system_label));
+        String appName = system
+                ? context.getString(com.android.internal.R.string.notification_app_name_system)
+                : context.getString(com.android.internal.R.string.notification_app_name_settings);
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
 
         n.addExtras(extras);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 91edfda..391843c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -100,10 +100,10 @@
     }
 
     public ScrimController createScrimController(LightBarController lightBarController,
-            ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
-            LockscreenWallpaper lockscreenWallpaper, Consumer<Integer> scrimVisibleListener,
-            DozeParameters dozeParameters, AlarmManager alarmManager) {
-        return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+            ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper,
+            Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
+            AlarmManager alarmManager) {
+        return new ScrimController(lightBarController, scrimBehind, scrimInFront,
                 scrimVisibleListener, dozeParameters, alarmManager);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 092f3d2..5bf62f6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -22,8 +22,10 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.os.Handler;
+import android.os.PowerManager;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
@@ -46,7 +48,7 @@
 
         DozeHost host = getHost(dozeService);
         AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
-        DozeParameters params = new DozeParameters(context);
+        DozeParameters params = DozeParameters.getInstance(context);
         Handler handler = new Handler();
         WakeLock wakeLock = new DelayedWakeLock(handler,
                 WakeLock.createPartial(context, "Doze"));
@@ -64,9 +66,9 @@
                 createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
                         handler, wakeLock, machine),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
-                new DozeScreenState(wrappedService, handler, params),
+                new DozeScreenState(wrappedService, handler, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
-                new DozeWallpaperState(context)
+                new DozeWallpaperState(context, params)
         });
 
         return machine;
@@ -92,7 +94,8 @@
     private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
             DozeMachine machine, Handler handler, AlarmManager alarmManager,
             DozeParameters params) {
-        return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params);
+        return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
+                KeyguardUpdateMonitor.getInstance(context));
     }
 
     public static DozeHost getHost(DozeService service) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 6ff8e3d..152b9fc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -92,17 +92,17 @@
             switch (this) {
                 case UNINITIALIZED:
                 case INITIALIZED:
-                case DOZE:
+                case DOZE_REQUEST_PULSE:
+                    return parameters.shouldControlScreenOff() ? Display.STATE_ON
+                            : Display.STATE_OFF;
                 case DOZE_AOD_PAUSED:
+                case DOZE:
                     return Display.STATE_OFF;
                 case DOZE_PULSING:
                     return Display.STATE_ON;
                 case DOZE_AOD:
                 case DOZE_AOD_PAUSING:
                     return Display.STATE_DOZE_SUSPEND;
-                case DOZE_REQUEST_PULSE:
-                    return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF
-                            : Display.STATE_ON;
                 default:
                     return Display.STATE_UNKNOWN;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 7d14564..f72bff5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -21,6 +21,7 @@
 import android.view.Display;
 
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.wakelock.WakeLock;
 
 /**
  * Controls the screen when dozing.
@@ -30,18 +31,27 @@
     private static final boolean DEBUG = DozeService.DEBUG;
     private static final String TAG = "DozeScreenState";
 
+    /**
+     * Delay entering low power mode when animating to make sure that we'll have
+     * time to move all elements into their final positions while still at 60 fps.
+     */
+    private static final int ENTER_DOZE_DELAY = 3000;
+
     private final DozeMachine.Service mDozeService;
     private final Handler mHandler;
     private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
     private final DozeParameters mParameters;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
+    private boolean mWakeLockHeld;
+    private WakeLock mWakeLock;
 
     public DozeScreenState(DozeMachine.Service service, Handler handler,
-            DozeParameters parameters) {
+            DozeParameters parameters, WakeLock wakeLock) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
+        mWakeLock = wakeLock;
     }
 
     @Override
@@ -69,12 +79,33 @@
             // that the screen turns on again before the navigation bar is hidden. To work around
             // that, wait for a traversal to happen before applying the initial screen state.
             mPendingScreenState = screenState;
-            if (!messagePending) {
-                mHandler.post(mApplyPendingScreenState);
+
+            // Delay screen state transitions even longer while animations are running.
+            boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+                    && mParameters.shouldControlScreenOff();
+
+            if (!mWakeLockHeld && shouldDelayTransition) {
+                mWakeLockHeld = true;
+                mWakeLock.acquire();
             }
-            return;
+
+            if (!messagePending) {
+                if (DEBUG) {
+                    Log.d(TAG, "Display state changed to " + screenState + " delayed by "
+                            + (shouldDelayTransition ? ENTER_DOZE_DELAY : 1));
+                }
+
+                if (shouldDelayTransition) {
+                    mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
+                } else {
+                    mHandler.post(mApplyPendingScreenState);
+                }
+            } else if (DEBUG) {
+                Log.d(TAG, "Pending display state change to " + screenState);
+            }
+        } else {
+            applyScreenState(screenState);
         }
-        applyScreenState(screenState);
     }
 
     private void applyPendingScreenState() {
@@ -87,6 +118,10 @@
             if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
             mDozeService.setDozeScreenState(screenState);
             mPendingScreenState = Display.STATE_UNKNOWN;
+            if (mWakeLockHeld) {
+                mWakeLockHeld = false;
+                mWakeLock.release();
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 75f1b50..778e630 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -25,6 +25,9 @@
 import android.text.format.Formatter;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -44,22 +47,46 @@
     private final WakeLock mWakeLock;
     private final DozeMachine mMachine;
     private final AlarmTimeout mTimeTicker;
-    private final boolean mCanAnimateWakeup;
+    private final boolean mCanAnimateTransition;
+    private final DozeParameters mDozeParameters;
+
+    private boolean mKeyguardShowing;
+    private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
+            new KeyguardUpdateMonitorCallback() {
+
+                @Override
+                public void onKeyguardVisibilityChanged(boolean showing) {
+                    mKeyguardShowing = showing;
+                    updateAnimateScreenOff();
+                }
+            };
 
     private long mLastTimeTickElapsed = 0;
 
     public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
             WakeLock wakeLock, DozeHost host, Handler handler,
-            DozeParameters params) {
+            DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mMachine = machine;
         mWakeLock = wakeLock;
         mHost = host;
         mHandler = handler;
-        mCanAnimateWakeup = !params.getDisplayNeedsBlanking();
-
+        mCanAnimateTransition = !params.getDisplayNeedsBlanking();
+        mDozeParameters = params;
         mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
-        mHost.setAnimateScreenOff(params.getCanControlScreenOffAnimation());
+        keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+    }
+
+    /**
+     * Decide if we're taking over the screen-off animation
+     * when the device was configured to skip doze after screen off.
+     */
+    private void updateAnimateScreenOff() {
+        if (mCanAnimateTransition) {
+            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+            mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
+            mHost.setAnimateScreenOff(controlScreenOff);
+        }
     }
 
     private void pulseWhileDozing(int reason) {
@@ -118,7 +145,7 @@
                 // Keep current state.
                 break;
             default:
-                mHost.setAnimateWakeup(mCanAnimateWakeup);
+                mHost.setAnimateWakeup(mCanAnimateTransition && mDozeParameters.getAlwaysOn());
                 break;
         }
     }
@@ -170,4 +197,9 @@
 
         scheduleTimeTick();
     }
+
+    @VisibleForTesting
+    KeyguardUpdateMonitorCallback getKeyguardCallback() {
+        return mKeyguardVisibilityCallback;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 5156272..9d110fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -26,7 +26,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
 import java.io.PrintWriter;
 
@@ -43,10 +42,10 @@
     private boolean mIsAmbientMode;
     private final DozeParameters mDozeParameters;
 
-    public DozeWallpaperState(Context context) {
+    public DozeWallpaperState(Context context, DozeParameters dozeParameters) {
         this(IWallpaperManager.Stub.asInterface(
                 ServiceManager.getService(Context.WALLPAPER_SERVICE)),
-                new DozeParameters(context), KeyguardUpdateMonitor.getInstance(context));
+                dozeParameters, KeyguardUpdateMonitor.getInstance(context));
     }
 
     @VisibleForTesting
@@ -80,7 +79,7 @@
 
         final boolean animated;
         if (isAmbientMode) {
-            animated = mDozeParameters.getCanControlScreenOffAnimation() && !mKeyguardVisible;
+            animated = mDozeParameters.shouldControlScreenOff();
         } else {
             animated = !mDozeParameters.getDisplayNeedsBlanking();
         }
@@ -88,8 +87,10 @@
         if (isAmbientMode != mIsAmbientMode) {
             mIsAmbientMode = isAmbientMode;
             try {
-                Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode
-                        + ", animated: " + animated);
+                if (DEBUG) {
+                    Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
+                            + ", animated: " + animated);
+                }
                 mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
             } catch (RemoteException e) {
                 // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 951c0ea..59c7f23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -41,21 +41,33 @@
     }
 
     public void dispatchStartedWakingUp() {
+        if (getWakefulness() == WAKEFULNESS_WAKING) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_WAKING);
         dispatch(Observer::onStartedWakingUp);
     }
 
     public void dispatchFinishedWakingUp() {
+        if (getWakefulness() == WAKEFULNESS_AWAKE) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_AWAKE);
         dispatch(Observer::onFinishedWakingUp);
     }
 
     public void dispatchStartedGoingToSleep() {
+        if (getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
         dispatch(Observer::onStartedGoingToSleep);
     }
 
     public void dispatchFinishedGoingToSleep() {
+        if (getWakefulness() == WAKEFULNESS_ASLEEP) {
+            return;
+        }
         setWakefulness(WAKEFULNESS_ASLEEP);
         dispatch(Observer::onFinishedGoingToSleep);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 24d0126..2af7ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -133,6 +133,13 @@
         }
 
         @Override
+        public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+            mHandler.post(() -> {
+               mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+            });
+        }
+
+        @Override
         public void onMinimizedStateChanged(boolean isMinimized) {
             mHandler.post(() -> {
                 mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
@@ -141,10 +148,11 @@
 
         @Override
         public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
-                Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
+                Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
+                int displayRotation) {
             mHandler.post(() -> {
                 mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
-                        fromImeAdjustement, displayRotation);
+                        fromImeAdjustment, fromShelfAdjustment, displayRotation);
             });
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 21a836c..31d8cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
@@ -71,7 +69,7 @@
     private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
     private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
     private static final int MINIMIZE_STACK_MAX_DURATION = 200;
-    private static final int IME_SHIFT_DURATION = 300;
+    private static final int SHIFT_DURATION = 300;
 
     // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
     private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
@@ -354,11 +352,11 @@
     }
 
     /**
-     * Animates the PiP to offset it from the IME.
+     * Animates the PiP to offset it from the IME or shelf.
      */
-    void animateToIMEOffset(Rect toBounds) {
+    void animateToOffset(Rect toBounds) {
         cancelAnimations();
-        resizeAndAnimatePipUnchecked(toBounds, IME_SHIFT_DURATION);
+        resizeAndAnimatePipUnchecked(toBounds, SHIFT_DURATION);
     }
 
     /**
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 77931e4..3ba3d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -120,6 +120,8 @@
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
+    private boolean mIsShelfShowing;
+    private int mShelfHeight;
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
     private boolean mMovementWithinMinimize;
@@ -249,13 +251,20 @@
         mImeHeight = imeHeight;
     }
 
+    public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+        mIsShelfShowing = shelfVisible;
+        mShelfHeight = shelfHeight;
+    }
+
     public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
-            boolean fromImeAdjustement, int displayRotation) {
+            boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
+        final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
+
         // Re-calculate the expanded bounds
         mNormalBounds = normalBounds;
         Rect normalMovementBounds = new Rect();
         mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
-                mIsImeShowing ? mImeHeight : 0);
+                bottomOffset);
 
         // Calculate the expanded size
         float aspectRatio = (float) normalBounds.width() / normalBounds.height();
@@ -266,40 +275,23 @@
         mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
         Rect expandedMovementBounds = new Rect();
         mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
-                mIsImeShowing ? mImeHeight : 0);
+                bottomOffset);
 
-        // If this is from an IME adjustment, then we should move the PiP so that it is not occluded
-        // by the IME
-        if (fromImeAdjustement) {
+        // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
+        // occluded by the IME or shelf.
+        if (fromImeAdjustment || fromShelfAdjustment) {
             if (mTouchState.isUserInteracting()) {
                 // Defer the update of the current movement bounds until after the user finishes
                 // touching the screen
             } else {
-                final Rect bounds = new Rect(animatingBounds);
                 final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
                         ? expandedMovementBounds
                         : normalMovementBounds;
-                if (mIsImeShowing) {
-                    // IME visible, apply the IME offset if the space allows for it
-                    final int imeOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
-                            toMovementBounds.bottom - mImeOffset);
-                    if (bounds.top == mMovementBounds.bottom) {
-                        // If the PIP is currently resting on top of the IME, then adjust it with
-                        // the showing IME
-                        bounds.offsetTo(bounds.left, toMovementBounds.bottom - imeOffset);
-                    } else {
-                        bounds.offset(0, Math.min(0, toMovementBounds.bottom - imeOffset
-                                - bounds.top));
-                    }
-                } else {
-                    // IME hidden
-                    if (bounds.top >= (mMovementBounds.bottom - mImeOffset)) {
-                        // If the PIP is resting on top of the IME, then adjust it with the hiding
-                        // IME
-                        bounds.offsetTo(bounds.left, toMovementBounds.bottom);
-                    }
-                }
-                mMotionHelper.animateToIMEOffset(bounds);
+                animateToOffset(animatingBounds, toMovementBounds,
+                        fromImeAdjustment,
+                        fromImeAdjustment ? mIsImeShowing : mIsShelfShowing,
+                        // Shelf height serves as an offset, but does not change movement bounds.
+                        fromImeAdjustment ? mImeOffset : mShelfHeight);
             }
         }
 
@@ -321,6 +313,26 @@
         }
     }
 
+    private void animateToOffset(Rect animatingBounds, Rect toMovementBounds,
+            boolean fromImeAdjustment, boolean showing, int offset) {
+        final Rect bounds = new Rect(animatingBounds);
+        if (showing) {
+            // IME/shelf visible, apply the IME/shelf offset if the space allows for it
+            final int calculatedOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
+                    toMovementBounds.bottom - offset);
+            bounds.offset(0,
+                    Math.min(0, toMovementBounds.bottom - calculatedOffset - bounds.top));
+        } else {
+            // IME/shelf hidden
+            if (bounds.top >= (mMovementBounds.bottom - offset)) {
+                bounds.offset(0, toMovementBounds.bottom - bounds.top -
+                        // Counter going back home from search where keyboard is up.
+                        (fromImeAdjustment ? mShelfHeight : 0));
+            }
+        }
+        mMotionHelper.animateToOffset(bounds);
+    }
+
     private void onRegistrationChanged(boolean isRegistered) {
         mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
                 ? new PipAccessibilityInteractionConnection(mMotionHelper,
@@ -801,6 +813,8 @@
         pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
+        pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
+        pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
         pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + ENABLE_DISMISS_DRAG_TO_EDGE);
         pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index a984680..d6f6760 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -200,11 +200,15 @@
         }
 
         @Override
+        public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
+
+        @Override
         public void onMinimizedStateChanged(boolean isMinimized) {}
 
         @Override
         public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
-                Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
+                Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
+                int displayRotation) {
             mHandler.post(() -> {
                 mDefaultPipBounds.set(normalBounds);
             });
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 3a2b12f..b89e15d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioAttributes;
-import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -37,6 +36,7 @@
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.settingslib.Utils;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.utils.PowerUtil;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
@@ -72,6 +72,8 @@
             "PNW.clickedThermalShutdownWarning";
     private static final String ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING =
             "PNW.dismissedThermalShutdownWarning";
+    private static final String ACTION_SHOW_START_SAVER_CONFIRMATION =
+            BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION;
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -177,7 +179,7 @@
                         .setContentText(mContext.getString(R.string.invalid_charger_text))
                         .setColor(mContext.getColor(
                                 com.android.internal.R.color.system_notification_accent_color));
-        SystemUI.overrideNotificationAppName(mContext, nb);
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
@@ -222,7 +224,7 @@
                 pendingBroadcast(ACTION_START_SAVER));
         nb.setOnlyAlertOnce(!mPlaySound);
         mPlaySound = false;
-        SystemUI.overrideNotificationAppName(mContext, nb);
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
@@ -289,7 +291,7 @@
                         .setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
                         .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
                         .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
-        SystemUI.overrideNotificationAppName(mContext, nb);
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
     }
@@ -339,7 +341,7 @@
                         .setDeleteIntent(
                                 pendingBroadcast(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING))
                         .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
-        SystemUI.overrideNotificationAppName(mContext, nb);
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.notifyAsUser(
                 TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, n, UserHandle.ALL);
@@ -404,7 +406,7 @@
         d.setTitle(R.string.battery_saver_confirmation_title);
         d.setMessage(com.android.internal.R.string.battery_saver_description);
         d.setNegativeButton(android.R.string.cancel, null);
-        d.setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverMode);
+        d.setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverModeNoConfirmation);
         d.setShowForAllUsers(true);
         d.setOnDismissListener(new OnDismissListener() {
             @Override
@@ -416,8 +418,8 @@
         mSaverConfirmation = d;
     }
 
-    private void setSaverMode(boolean mode) {
-        mPowerMan.setPowerSaveMode(mode);
+    private void setSaverMode(boolean mode, boolean needFirstTimeWarning) {
+        BatterySaverUtils.setPowerSaveMode(mContext, mode, needFirstTimeWarning);
     }
 
     private final class Receiver extends BroadcastReceiver {
@@ -431,8 +433,9 @@
             filter.addAction(ACTION_DISMISSED_TEMP_WARNING);
             filter.addAction(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING);
             filter.addAction(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING);
+            filter.addAction(ACTION_SHOW_START_SAVER_CONFIRMATION);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
-                    android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
+                    android.Manifest.permission.DEVICE_POWER, mHandler);
         }
 
         @Override
@@ -443,6 +446,9 @@
                 dismissLowBatteryNotification();
                 mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT);
             } else if (action.equals(ACTION_START_SAVER)) {
+                setSaverMode(true, true);
+                dismissLowBatteryNotification();
+            } else if (action.equals(ACTION_SHOW_START_SAVER_CONFIRMATION)) {
                 dismissLowBatteryNotification();
                 showStartSaverConfirmation();
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
@@ -461,15 +467,6 @@
         }
     }
 
-    private final OnClickListener mStartSaverMode = new OnClickListener() {
-        @Override
-        public void onClick(DialogInterface dialog, int which) {
-            AsyncTask.execute(new Runnable() {
-                @Override
-                public void run() {
-                    setSaverMode(true);
-                }
-            });
-        }
-    };
+    private final OnClickListener mStartSaverModeNoConfirmation =
+            (dialog, which) -> setSaverMode(true, false);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index ac86c8a..f08219a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -137,24 +137,9 @@
     void updateBatteryWarningLevels() {
         int critLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-
-        final ContentResolver resolver = mContext.getContentResolver();
-        final int defWarnLevel = mContext.getResources().getInteger(
+        int warnLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
-        final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
-                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
 
-        // Note LOW_POWER_MODE_TRIGGER_LEVEL can take any value between 0 and 100, but
-        // for the UI purposes, let's cap it at 15% -- i.e. even if the trigger level is higher
-        // like 50%, let's not show the "low battery" notification until it hits
-        // config_lowBatteryWarningLevel, which is 15% by default.
-        // LOW_POWER_MODE_TRIGGER_LEVEL is still used in other places as-is. For example, if it's
-        // 50, then battery saver kicks in when the battery level hits 50%.
-        int warnLevel =  Math.min(defWarnLevel, lowPowerModeTriggerLevel);
-
-        if (warnLevel == 0) {
-            warnLevel = defWarnLevel;
-        }
         if (warnLevel < critLevel) {
             warnLevel = critLevel;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ea3a60b..8923952 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -42,16 +42,14 @@
     };
 
 
-    private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
-    private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
+    private final ArrayList<TileRecord> mTiles = new ArrayList<>();
+    private final ArrayList<TilePage> mPages = new ArrayList<>();
 
     private PageIndicator mPageIndicator;
 
     private int mNumPages;
     private PageListener mPageListener;
 
-    private int mPosition;
-    private boolean mOffPage;
     private boolean mListening;
     private Scroller mScroller;
 
@@ -85,16 +83,12 @@
     public void setListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
-        if (mListening) {
-            setPageListening(mPosition, true);
-            if (mOffPage) {
-                setPageListening(mPosition + 1, true);
-            }
-        } else {
-            // Make sure no pages are listening.
-            for (int i = 0; i < mPages.size(); i++) {
-                mPages.get(i).setListening(false);
-            }
+        updateListening();
+    }
+
+    private void updateListening() {
+        for (TilePage tilePage : mPages) {
+            tilePage.setListening(tilePage.getParent() == null ? false : mListening);
         }
     }
 
@@ -137,43 +131,6 @@
         super.computeScroll();
     }
 
-    /**
-     * Sets individual pages to listening or not.  If offPage it will set
-     * the next page after position to listening as well since we are in between
-     * pages.
-     */
-    private void setCurrentPage(int position, boolean offPage) {
-        if (mPosition == position && mOffPage == offPage) return;
-        if (mListening) {
-            if (mPosition != position) {
-                // Clear out the last pages from listening.
-                setPageListening(mPosition, false);
-                if (mOffPage) {
-                    setPageListening(mPosition + 1, false);
-                }
-                // Set the new pages to listening
-                setPageListening(position, true);
-                if (offPage) {
-                    setPageListening(position + 1, true);
-                }
-            } else if (mOffPage != offPage) {
-                // Whether we are showing position + 1 has changed.
-                setPageListening(mPosition + 1, offPage);
-            }
-        }
-        // Save the current state.
-        mPosition = position;
-        mOffPage = offPage;
-    }
-
-    private void setPageListening(int position, boolean listening) {
-        if (position >= mPages.size()) return;
-        if (isLayoutRtl()) {
-            position = mPages.size() - 1 - position;
-        }
-        mPages.get(position).setListening(listening);
-    }
-
     @Override
     public boolean hasOverlappingRendering() {
         return false;
@@ -362,7 +319,6 @@
                 public void onPageScrolled(int position, float positionOffset,
                         int positionOffsetPixels) {
                     if (mPageIndicator == null) return;
-                    setCurrentPage(position, positionOffset != 0);
                     mPageIndicator.setLocation(position + positionOffset);
                     if (mPageListener != null) {
                         mPageListener.onPageChanged(positionOffsetPixels == 0 &&
@@ -407,11 +363,14 @@
     }
 
     private final PagerAdapter mAdapter = new PagerAdapter() {
+        @Override
         public void destroyItem(ViewGroup container, int position, Object object) {
             if (DEBUG) Log.d(TAG, "Destantiating " + position);
             container.removeView((View) object);
+            updateListening();
         }
 
+        @Override
         public Object instantiateItem(ViewGroup container, int position) {
             if (DEBUG) Log.d(TAG, "Instantiating " + position);
             if (isLayoutRtl()) {
@@ -419,6 +378,7 @@
             }
             ViewGroup view = mPages.get(position);
             container.addView(view);
+            updateListening();
             return view;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7da109d..bfbfbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -140,7 +140,6 @@
         setMargins(mQSFooter);
         setMargins(mQSPanel);
         setMargins(mHeader);
-        setMargins(mQSCustomizer);
     }
 
     private void setMargins(View view) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index dbf1745..0fa6597 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.graphics.PorterDuff.Mode;
@@ -49,7 +50,6 @@
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
 import com.android.systemui.statusbar.phone.SettingsButton;
@@ -257,17 +257,31 @@
         mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
-
         final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
-
-
-        mMultiUserSwitch.setVisibility(mExpanded
-                && UserManager.get(mContext).isUserSwitcherEnabled()
-                ? View.VISIBLE : View.INVISIBLE);
-
+        mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
         mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
     }
 
+    private boolean showUserSwitcher(boolean isDemo) {
+        if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
+            return false;
+        }
+        UserManager userManager = UserManager.get(mContext);
+        if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
+            return false;
+        }
+        int switchableUserCount = 0;
+        for (UserInfo user : userManager.getUsers(true)) {
+            if (user.supportsSwitchToByUser()) {
+                ++switchableUserCount;
+                if (switchableUserCount > 1) {
+                    return true;
+                }
+            }
+        }
+        return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
+    }
+
     private void updateListeners() {
         if (mListening) {
             mUserInfoController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 9759b69..eb95866 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -15,42 +15,33 @@
 package com.android.systemui.qs.customize;
 
 import android.content.Context;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
-import com.android.systemui.R;
+
 import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.tileimpl.QSTileView;
-import java.util.Objects;
 
 public class CustomizeTileView extends QSTileView {
+    private boolean mShowAppLabel;
 
-    private TextView mAppLabel;
-    private int mLabelMinLines;
     public CustomizeTileView(Context context, QSIconView icon) {
         super(context, icon);
     }
 
-    @Override
-    protected void createLabel() {
-        super.createLabel();
-        mLabelMinLines = mLabel.getMinLines();
-        mAppLabel = findViewById(R.id.app_label);
-        mAppLabel.setAlpha(.6f);
-    }
-
     public void setShowAppLabel(boolean showAppLabel) {
-        mAppLabel.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
+        mShowAppLabel = showAppLabel;
+        mSecondLine.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
         mLabel.setSingleLine(showAppLabel);
     }
 
-    public void setAppLabel(CharSequence label) {
-        if (!Objects.equals(label, mAppLabel.getText())) {
-            mAppLabel.setText(label);
-        }
+    @Override
+    protected void handleStateChanged(QSTile.State state) {
+        super.handleStateChanged(state);
+        mSecondLine.setVisibility(mShowAppLabel ? View.VISIBLE : View.GONE);
     }
 
     public TextView getAppLabel() {
-        return mAppLabel;
+        return mSecondLine;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index bdc5e7d..441d29b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -42,19 +42,19 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.R;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
+import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.customize.TileAdapter.Holder;
 import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
 import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
 import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.tileimpl.QSIconViewImpl;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
-
+    private static final int MIN_NUM_TILES = 6;
     private static final long DRAG_LENGTH = 100;
     private static final float DRAG_SCALE = 1.2f;
     public static final long MOVE_DURATION = 150;
@@ -219,9 +219,15 @@
             return;
         }
         if (holder.getItemViewType() == TYPE_EDIT) {
-            ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
-                    mCurrentDrag != null ? R.string.drag_to_remove_tiles
-                    : R.string.drag_to_add_tiles);
+            final int titleResId;
+            if (mCurrentDrag == null) {
+                titleResId = R.string.drag_to_add_tiles;
+            } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) {
+                titleResId = R.string.drag_to_remove_disabled;
+            } else {
+                titleResId = R.string.drag_to_remove_tiles;
+            }
+            ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId);
             return;
         }
         if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -268,7 +274,6 @@
                     R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
         }
         holder.mTileView.onStateChanged(info.state);
-        holder.mTileView.setAppLabel(info.appLabel);
         holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
 
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
@@ -286,7 +291,7 @@
                         if (mAccessibilityMoving) {
                             selectPosition(position, v);
                         } else {
-                            if (position < mEditIndex) {
+                            if (position < mEditIndex && canRemoveTiles()) {
                                 showAccessibilityDialog(position, v);
                             } else {
                                 startAccessibleDrag(position);
@@ -297,6 +302,10 @@
             }
         }
     }
+    
+    private boolean canRemoveTiles() {
+        return mCurrentSpecs.size() > MIN_NUM_TILES;
+    }
 
     private void selectPosition(int position, View v) {
         // Remove the placeholder.
@@ -507,7 +516,7 @@
                 break;
             }
         }
-    };
+    }
 
     private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
 
@@ -551,6 +560,9 @@
         @Override
         public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
                 ViewHolder target) {
+            if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) {
+                return target.getAdapterPosition() < mEditIndex;
+            }
             return target.getAdapterPosition() <= mEditIndex + 1;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 2ac592f..8bf4096 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.TileService;
+import android.text.TextUtils;
 import android.widget.Button;
 
 import com.android.systemui.Dependency;
@@ -169,7 +170,8 @@
         info.state.expandedAccessibilityClassName =
                 Button.class.getName();
         info.spec = spec;
-        info.appLabel = appLabel;
+        info.state.secondaryLabel = (isSystem || TextUtils.equals(state.label, appLabel))
+                ? null : appLabel;
         info.isSystem = isSystem;
         mTiles.add(info);
         mSpecs.add(spec);
@@ -186,7 +188,6 @@
 
     public static class TileInfo {
         public String spec;
-        public CharSequence appLabel;
         public QSTile.State state;
         public boolean isSystem;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 8d99303..ae99786 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -348,7 +348,7 @@
         return ComponentName.unflattenFromString(action);
     }
 
-    public static QSTile create(QSTileHost host, String spec) {
+    public static CustomTile create(QSTileHost host, String spec) {
         if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
             throw new IllegalArgumentException("Bad custom tile spec: " + spec);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 77c3bfa..8d48890 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -52,6 +52,14 @@
     }
 
     public QSTile createTile(String tileSpec) {
+        QSTileImpl tile = createTileInternal(tileSpec);
+        if (tile != null) {
+            tile.handleStale(); // Tile was just created, must be stale.
+        }
+        return tile;
+    }
+
+    private QSTileImpl createTileInternal(String tileSpec) {
         if (tileSpec.equals("wifi")) return new WifiTile(mHost);
         else if (tileSpec.equals("bt")) return new BluetoothTile(mHost);
         else if (tileSpec.equals("cell")) return new CellularTile(mHost);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 24ddafc..834feb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -85,6 +85,7 @@
     private String mTileSpec;
     private EnforcedAdmin mEnforcedAdmin;
     private boolean mShowingDetail;
+    private int mIsFullQs;
 
     public abstract TState newTileState();
 
@@ -103,7 +104,6 @@
     protected QSTileImpl(QSHost host) {
         mHost = host;
         mContext = host.getContext();
-        handleStale(); // Tile was just created, must be stale.
     }
 
     /**
@@ -111,18 +111,7 @@
      * listening client it will go into the listening state.
      */
     public void setListening(Object listener, boolean listening) {
-        if (listening) {
-            if (mListeners.add(listener) && mListeners.size() == 1) {
-                if (DEBUG) Log.d(TAG, "setListening " + true);
-                mHandler.obtainMessage(H.SET_LISTENING, 1, 0).sendToTarget();
-                refreshState(); // Ensure we get at least one refresh after listening.
-            }
-        } else {
-            if (mListeners.remove(listener) && mListeners.size() == 0) {
-                if (DEBUG) Log.d(TAG, "setListening " + false);
-                mHandler.obtainMessage(H.SET_LISTENING, 0, 0).sendToTarget();
-            }
-        }
+        mHandler.obtainMessage(H.SET_LISTENING, listening ? 1 : 0, 0, listener).sendToTarget();
     }
 
     protected long getStaleTimeout() {
@@ -206,19 +195,10 @@
             logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
         }
         return logMaker.setSubtype(getMetricsCategory())
-                .addTaggedData(FIELD_CONTEXT, isFullQs())
+                .addTaggedData(FIELD_CONTEXT, mIsFullQs)
                 .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
     }
 
-    private int isFullQs() {
-        for (Object listener : mListeners) {
-            if (TilePage.class.equals(listener.getClass())) {
-                return 1;
-            }
-        }
-        return 0;
-    }
-
     public void showDetail(boolean show) {
         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
     }
@@ -353,6 +333,32 @@
         handleRefreshState(null);
     }
 
+    private void handleSetListeningInternal(Object listener, boolean listening) {
+        if (listening) {
+            if (mListeners.add(listener) && mListeners.size() == 1) {
+                if (DEBUG) Log.d(TAG, "handleSetListening true");
+                handleSetListening(listening);
+                refreshState(); // Ensure we get at least one refresh after listening.
+            }
+        } else {
+            if (mListeners.remove(listener) && mListeners.size() == 0) {
+                if (DEBUG) Log.d(TAG, "handleSetListening false");
+                handleSetListening(listening);
+            }
+        }
+        updateIsFullQs();
+    }
+
+    private void updateIsFullQs() {
+        for (Object listener : mListeners) {
+            if (TilePage.class.equals(listener.getClass())) {
+                mIsFullQs = 1;
+                return;
+            }
+        }
+        mIsFullQs = 0;
+    }
+
     protected abstract void handleSetListening(boolean listening);
 
     protected void handleDestroy() {
@@ -465,8 +471,8 @@
                     name = "handleClearState";
                     handleClearState();
                 } else if (msg.what == SET_LISTENING) {
-                    name = "handleSetListening";
-                    handleSetListening(msg.arg1 != 0);
+                    name = "handleSetListeningInternal";
+                    handleSetListeningInternal(msg.obj, msg.arg1 != 0);
                 } else if (msg.what == STALE) {
                     name = "handleStale";
                     handleStale();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 45c20a0..4774785 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -40,7 +40,7 @@
     private static final boolean DUAL_TARGET_ALLOWED = false;
     private View mDivider;
     protected TextView mLabel;
-    private TextView mSecondLine;
+    protected TextView mSecondLine;
     private ImageView mPadLock;
     private int mState;
     private ViewGroup mLabelContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 3b79db7..06183e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -78,7 +78,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (mController == null) return;
         if (listening) {
             mController.addCallback(mCallback);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 678aa71..ed78048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -83,7 +83,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (mController == null) return;
         if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
         if (listening) {
             mController.addCallback(mCallback);
@@ -98,7 +97,6 @@
     @Override
     protected void handleUserSwitch(int newUserId) {
         super.handleUserSwitch(newUserId);
-        if (mController == null) return;
         mController.setCurrentUserId(newUserId);
     }
 
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 8427e32..7dcf5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -259,7 +259,6 @@
     public void handleSetListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
-        if (mController == null) return;
         if (mListening) {
             mController.addCallback(mZenCallback);
             Prefs.registerListener(mContext, mPrefListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 4f4004c..3c565ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -64,7 +64,7 @@
         mContext.unregisterReceiver(mReceiver);
     }
 
-    public static QSTile create(QSHost host, String spec) {
+    public static IntentTile create(QSHost host, String spec) {
         if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
             throw new IllegalArgumentException("Bad intent tile spec: " + spec);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 60422ee..28b047b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -51,7 +51,6 @@
     }
 
     public void handleSetListening(boolean listening) {
-        if (mController == null) return;
         if (listening) {
             mController.addCallback(mCallback);
         } else {
@@ -66,7 +65,6 @@
 
     @Override
     protected void handleClick() {
-        if (mController == null) return;
         final boolean newState = !mState.value;
         mController.setRotationLocked(!newState);
         refreshState(newState);
@@ -79,7 +77,6 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (mController == null) return;
         final boolean rotationLocked = mController.isRotationLocked();
 
         state.value = !rotationLocked;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 3ad3940..28fdc11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -76,7 +76,6 @@
 
     @Override
     public void handleSetListening(boolean listening) {
-        if (mController == null) return;
         if (listening) {
             mController.addCallback(mSignalCallback);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ac26f68..19da3db 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -276,6 +276,9 @@
         }
     };
 
+    // Used to reset the dummy stack view
+    private final TaskStack mEmptyTaskStack = new TaskStack();
+
     public RecentsImpl(Context context) {
         mContext = context;
         mHandler = new Handler();
@@ -1108,6 +1111,10 @@
             }
         });
         EventBus.getDefault().send(hideMenuEvent);
+
+        // Once we have launched the activity, reset the dummy stack view tasks so we don't hold
+        // onto references to the same tasks consumed by the activity
+        mDummyStackView.setTasks(mEmptyTaskStack, false /* notifyStackChanges */);
     }
 
     /**** OnAnimationFinishedListener Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index c348187..75bc9558 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -96,6 +96,9 @@
         public void onTaskStackChanged() {
             ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance()
                     .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */);
+            if (info == null) {
+                return;
+            }
             if (mBlacklistedPackages.contains(info.baseActivity.getPackageName())) {
                 hide(true);
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9793b1f..068fd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.screenshot;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
 import static com.android.systemui.screenshot.GlobalScreenshot.SHARING_INTENT;
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
 
@@ -26,11 +28,11 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.admin.DevicePolicyManager;
 import android.app.Notification;
 import android.app.Notification.BigPictureStyle;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -58,7 +60,6 @@
 import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -195,7 +196,7 @@
                         .setShowWhen(true)
                         .setColor(r.getColor(
                                 com.android.internal.R.color.system_notification_accent_color));
-        SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder);
+        SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder, true);
 
         mNotificationBuilder = new Notification.Builder(context,
                 NotificationChannels.SCREENSHOTS_HEADSUP)
@@ -210,7 +211,7 @@
             .setStyle(mNotificationStyle)
             .setPublicVersion(mPublicNotificationBuilder.build());
         mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
-        SystemUI.overrideNotificationAppName(context, mNotificationBuilder);
+        SystemUI.overrideNotificationAppName(context, mNotificationBuilder, true);
 
         mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
                 mNotificationBuilder.build());
@@ -517,6 +518,7 @@
                     | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
                 PixelFormat.TRANSLUCENT);
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
+        mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mNotificationManager =
             (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -889,7 +891,7 @@
             b.setContentIntent(pendingIntent);
         }
 
-        SystemUI.overrideNotificationAppName(context, b);
+        SystemUI.overrideNotificationAppName(context, b, true);
 
         Notification n = new Notification.BigTextStyle(b)
                 .bigText(errorMsg)
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index cb9453b..b7a5d31 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
@@ -55,6 +56,7 @@
         mLp.token = new Binder();
         mLp.setTitle(WINDOW_TITLE);
         mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 0876507..8b6b5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -26,6 +26,7 @@
 import android.graphics.Color;
 import android.graphics.RectF;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -98,7 +99,6 @@
     private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
             = new PathInterpolator(0, 0, 0.5f, 1);
     private final int mTintedRippleColor;
-    private final int mLowPriorityRippleColor;
     protected final int mNormalRippleColor;
     private final AccessibilityManager mAccessibilityManager;
     private final DoubleTapHelper mDoubleTapHelper;
@@ -133,7 +133,6 @@
     private float mAppearAnimationFraction = -1.0f;
     private float mAppearAnimationTranslation;
     private final int mNormalColor;
-    private final int mLowPriorityColor;
     private boolean mIsBelowSpeedBump;
     private FalsingManager mFalsingManager;
 
@@ -178,6 +177,10 @@
     private boolean mNeedsDimming;
     private int mDimmedAlpha;
     private boolean mBlockNextTouch;
+    private boolean mIsHeadsUpAnimation;
+    private int mHeadsUpAddStartLocation;
+    private float mHeadsUpLocation;
+    private boolean mIsAppearing;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -186,12 +189,8 @@
         setClipChildren(false);
         setClipToPadding(false);
         mNormalColor = context.getColor(R.color.notification_material_background_color);
-        mLowPriorityColor = context.getColor(
-                R.color.notification_material_background_low_priority_color);
         mTintedRippleColor = context.getColor(
                 R.color.notification_ripple_tinted_color);
-        mLowPriorityRippleColor = context.getColor(
-                R.color.notification_ripple_color_low_priority);
         mNormalRippleColor = context.getColor(
                 R.color.notification_ripple_untinted_color);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -204,6 +203,18 @@
                 makeInactive(true /* animate */);
             }
         }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+        initDimens();
+    }
+
+    private void initDimens() {
+        mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_start);
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initDimens();
     }
 
     @Override
@@ -745,27 +756,34 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAnimation;
+        mHeadsUpLocation = endLocation;
         if (mDrawingAppearAnimation) {
             startAppearAnimation(false /* isAppearing */, translationDirection,
-                    0, duration, onFinishedRunnable);
+                    delay, duration, onFinishedRunnable, animationListener);
         } else if (onFinishedRunnable != null) {
             onFinishedRunnable.run();
         }
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
         enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAppear;
+        mHeadsUpLocation = mHeadsUpAddStartLocation;
         if (mDrawingAppearAnimation) {
-            startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
+            startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+                    duration, null, null);
         }
     }
 
     private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
-            long duration, final Runnable onFinishedRunnable) {
+            long duration, final Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
         cancelAppearAnimation();
         mAnimationTranslationY = translationDirection * getActualHeight();
         if (mAppearAnimationFraction == -1.0f) {
@@ -778,6 +796,7 @@
                 mAppearAnimationTranslation = 0;
             }
         }
+        mIsAppearing = isAppearing;
 
         float targetValue;
         if (isAppearing) {
@@ -803,6 +822,9 @@
                 invalidate();
             }
         });
+        if (animationListener != null) {
+            mAppearAnimator.addListener(animationListener);
+        }
         if (delay > 0) {
             // we need to apply the initial state already to avoid drawn frames in the wrong state
             updateAppearAnimationAlpha();
@@ -862,9 +884,21 @@
                 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
         widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
         widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
-        float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
-                widthFraction);
-        float right = getWidth() - left;
+        float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
+        if (mIsHeadsUpAnimation && !mIsAppearing) {
+            startWidthFraction = 0;
+        }
+        float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
+                        * getWidth();
+        float left;
+        float right;
+        if (mIsHeadsUpAnimation) {
+            left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
+            right = left + width;
+        } else {
+            left = getWidth() * 0.5f - width / 2.0f;
+            right = getWidth() - left;
+        }
 
         // handle top animation
         float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
@@ -957,8 +991,6 @@
         }
         if (withTint && mBgTint != NO_COLOR) {
             return mBgTint;
-        } else if (mIsBelowSpeedBump) {
-            return mLowPriorityColor;
         } else {
             return mNormalColor;
         }
@@ -967,8 +999,6 @@
     protected int getRippleColor() {
         if (mBgTint != 0) {
             return mTintedRippleColor;
-        } else if (mIsBelowSpeedBump) {
-            return mLowPriorityRippleColor;
         } else {
             return mNormalRippleColor;
         }
@@ -1046,6 +1076,14 @@
         return calculateBgColor(false /* withTint */, false /* withOverride */);
     }
 
+    public boolean isPinned() {
+        return false;
+    }
+
+    public boolean isHeadsUpAnimatingAway() {
+        return false;
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index e8f0925..4728a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -28,11 +28,17 @@
     public static final long ANIMATION_DURATION_LENGTH = 210;
 
     public static void fadeOut(final View view, final Runnable endRunnable) {
+        fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable);
+    }
+
+    public static void fadeOut(final View view, long duration, int delay,
+            final Runnable endRunnable) {
         view.animate().cancel();
         view.animate()
                 .alpha(0f)
-                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setDuration(duration)
                 .setInterpolator(Interpolators.ALPHA_OUT)
+                .setStartDelay(delay)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
@@ -93,6 +99,10 @@
     }
 
     public static void fadeIn(final View view) {
+        fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
+    }
+
+    public static void fadeIn(final View view, long duration, int delay) {
         view.animate().cancel();
         if (view.getVisibility() == View.INVISIBLE) {
             view.setAlpha(0.0f);
@@ -100,7 +110,8 @@
         }
         view.animate()
                 .alpha(1f)
-                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setDuration(duration)
+                .setStartDelay(delay)
                 .setInterpolator(Interpolators.ALPHA_IN)
                 .withEndAction(null);
         if (view.hasOverlappingRendering()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e5c5dcd..6b9567d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -88,6 +88,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
 
 public class ExpandableNotificationRow extends ActivatableNotificationView
         implements PluginListener<NotificationMenuRowPlugin> {
@@ -139,6 +140,7 @@
     private boolean mSensitiveHiddenInGeneral;
     private boolean mShowingPublicInitialized;
     private boolean mHideSensitiveForIntrinsicHeight;
+    private float mHeaderVisibleAmount = 1.0f;
 
     /**
      * Is this notification expanded by the system. The expansion state can be overridden by the
@@ -156,8 +158,6 @@
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
     private NotificationContentView[] mLayouts;
-    private int mMaxExpandHeight;
-    private int mHeadsUpHeight;
     private int mNotificationColor;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
@@ -180,6 +180,7 @@
     private boolean mExpandAnimationRunning;
     private AboveShelfChangedListener mAboveShelfChangedListener;
     private HeadsUpManager mHeadsUpManager;
+    private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
     private View mHelperButton;
     private boolean mChildIsExpanding;
 
@@ -534,6 +535,30 @@
         addChildNotification(row, -1);
     }
 
+    /**
+     * Set the how much the header should be visible. A value of 0 will make the header fully gone
+     * and a value of 1 will make the notification look just like normal.
+     * This is being used for heads up notifications, when they are pinned to the top of the screen
+     * and the header content is extracted to the statusbar.
+     *
+     * @param headerVisibleAmount the amount the header should be visible.
+     */
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mHeaderVisibleAmount != headerVisibleAmount) {
+            mHeaderVisibleAmount = headerVisibleAmount;
+            mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
+            if (mChildrenContainer != null) {
+                mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
+            }
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
+    @Override
+    public float getHeaderVisibleAmount() {
+        return mHeaderVisibleAmount;
+    }
+
     @Override
     public void setHeadsUpIsVisible() {
         super.setHeadsUpIsVisible();
@@ -722,6 +747,7 @@
         }
     }
 
+    @Override
     public boolean isPinned() {
         return mIsPinned;
     }
@@ -741,11 +767,11 @@
             return mChildrenContainer.getIntrinsicHeight();
         }
         if(mExpandedWhenPinned) {
-            return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+            return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
         } else if (atLeastMinHeight) {
-            return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+            return Math.max(getCollapsedHeight(), getHeadsUpHeight());
         } else {
-            return mHeadsUpHeight;
+            return getHeadsUpHeight();
         }
     }
 
@@ -1034,11 +1060,12 @@
         }
     }
 
-    public void setDismissed(boolean dismissed, boolean fromAccessibility) {
-        mDismissed = dismissed;
+    public void setDismissed(boolean fromAccessibility) {
+        mDismissed = true;
         mGroupParentWhenDismissed = mNotificationParent;
         mRefocusOnDismiss = fromAccessibility;
         mChildAfterViewWhenDismissed = null;
+        mEntry.icon.setDismissed();
         if (isChildInGroup()) {
             List<ExpandableNotificationRow> notificationChildren =
                     mNotificationParent.getNotificationChildren();
@@ -1090,17 +1117,26 @@
 
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         boolean wasAboveShelf = isAboveShelf();
+        boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
         mHeadsupDisappearRunning = headsUpAnimatingAway;
         mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+        if (changed && mHeadsUpAnimatingAwayListener != null) {
+            mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
+        }
         if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
         }
     }
 
+    public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
+        mHeadsUpAnimatingAwayListener = listener;
+    }
+
     /**
      * @return if the view was just heads upped and is now animating away. During such a time the
      * layout needs to be kept consistent
      */
+    @Override
     public boolean isHeadsUpAnimatingAway() {
         return mHeadsupDisappearRunning;
     }
@@ -1121,7 +1157,7 @@
                 groupSummary.performDismiss(fromAccessibility);
             }
         }
-        setDismissed(true, fromAccessibility);
+        setDismissed(fromAccessibility);
         if (isClearable()) {
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
@@ -1921,9 +1957,9 @@
             if (isPinned() || mHeadsupDisappearRunning) {
                 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
             } else if (isExpanded()) {
-                return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+                return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
             } else {
-                return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+                return Math.max(getCollapsedHeight(), getHeadsUpHeight());
             }
         } else if (isExpanded()) {
             return getMaxExpandHeight();
@@ -1997,8 +2033,11 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int intrinsicBefore = getIntrinsicHeight();
         super.onLayout(changed, left, top, right, bottom);
-        updateMaxHeights();
+        if (intrinsicBefore != getIntrinsicHeight()) {
+            notifyHeightChanged(true  /* needsAnimation */);
+        }
         if (mMenuRow.getMenuView() != null) {
             mMenuRow.onHeightUpdate();
         }
@@ -2022,15 +2061,6 @@
         }
     }
 
-    public void updateMaxHeights() {
-        int intrinsicBefore = getIntrinsicHeight();
-        mMaxExpandHeight = mPrivateLayout.getExpandHeight();
-        mHeadsUpHeight = mPrivateLayout.getHeadsUpHeight();
-        if (intrinsicBefore != getIntrinsicHeight()) {
-            notifyHeightChanged(true  /* needsAnimation */);
-        }
-    }
-
     @Override
     public void notifyHeightChanged(boolean needsAnimation) {
         super.notifyHeightChanged(needsAnimation);
@@ -2173,7 +2203,12 @@
     }
 
     public int getMaxExpandHeight() {
-        return mMaxExpandHeight;
+        return mPrivateLayout.getExpandHeight();
+    }
+
+
+    private int getHeadsUpHeight() {
+        return mPrivateLayout.getHeadsUpHeight();
     }
 
     public boolean areGutsExposed() {
@@ -2273,7 +2308,7 @@
         } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
             return mChildrenContainer.getMinHeight();
         } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
-            return mHeadsUpHeight;
+            return getHeadsUpHeight();
         }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMinHeight();
@@ -2319,10 +2354,6 @@
         }
     }
 
-    public boolean isMaxExpandHeightInitialized() {
-        return mMaxExpandHeight != 0;
-    }
-
     public NotificationContentView getShowingLayout() {
         return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 8bc2201..67268c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -276,12 +276,18 @@
         setClipToOutline(mAlwaysRoundBothCorners);
     }
 
-    public void setTopRoundness(float topRoundness, boolean animate) {
+    /**
+     * Set the topRoundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setTopRoundness(float topRoundness, boolean animate) {
         if (mTopRoundness != topRoundness) {
             mTopRoundness = topRoundness;
             PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
                     ROUNDNESS_PROPERTIES, animate);
+            return true;
         }
+        return false;
     }
 
     protected void applyRoundness() {
@@ -305,12 +311,18 @@
         return mCurrentBottomRoundness * mOutlineRadius;
     }
 
-    public void setBottomRoundness(float bottomRoundness, boolean animate) {
+    /**
+     * Set the bottom roundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
         if (mBottomRoundness != bottomRoundness) {
             mBottomRoundness = bottomRoundness;
             PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
                     ROUNDNESS_PROPERTIES, animate);
+            return true;
         }
+        return false;
     }
 
     protected void setBackgroundTop(int backgroundTop) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 204adc8..df6a977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -296,19 +297,24 @@
 
     /**
      * Perform a remove animation on this view.
-     *
      * @param duration The duration of the remove animation.
+     * @param delay The delay of the animation
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
-     *                             animation should be performed. A value of -1 means that The
-     *                             remove animation should be performed upwards,
-     *                             such that the  child appears to be going away to the top. 1
-     *                             Should mean the opposite.
+ *                             animation should be performed. A value of -1 means that The
+ *                             remove animation should be performed upwards,
+ *                             such that the  child appears to be going away to the top. 1
+ *                             Should mean the opposite.
+     * @param isHeadsUpAnimation Is this a headsUp animation.
+     * @param endLocation The location where the horizonal heads up disappear animation should end.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
+     * @param animationListener An animation listener to add to the animation.
      */
-    public abstract void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable);
+    public abstract void performRemoveAnimation(long duration,
+            long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener);
 
-    public abstract void performAddAnimation(long delay, long duration);
+    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
 
     /**
      * Set the notification appearance to be below the speed bump.
@@ -390,6 +396,10 @@
         }
     }
 
+    public float getHeaderVisibleAmount() {
+        return 1.0f;
+    }
+
     protected boolean shouldClipToActualHeight() {
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
new file mode 100644
index 0000000..5e03fbf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+
+/**
+ * The view in the statusBar that contains part of the heads-up information
+ */
+public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
+    private int mAbsoluteStartPadding;
+    private int mEndMargin;
+    private View mIconPlaceholder;
+    private TextView mTextView;
+    private NotificationData.Entry mShowingEntry;
+    private Rect mLayoutedIconRect = new Rect();
+    private int[] mTmpPosition = new int[2];
+    private boolean mFirstLayout = true;
+    private boolean mPublicMode;
+    private int mMaxWidth;
+    private View mRootView;
+    private int mLeftInset;
+    private Rect mIconDrawingRect = new Rect();
+    private Runnable mOnDrawingRectChangedListener;
+
+    public HeadsUpStatusBarView(Context context) {
+        this(context, null);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = getResources();
+        mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
+            + res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_content_margin_start);
+        mEndMargin = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_end);
+        setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
+        updateMaxWidth();
+    }
+
+    private void updateMaxWidth() {
+        int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
+        if (maxWidth != mMaxWidth) {
+            // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
+            // notification panel
+            mMaxWidth = maxWidth;
+            requestLayout();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mMaxWidth > 0) {
+            int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
+            widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
+                    MeasureSpec.getMode(widthMeasureSpec));
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateMaxWidth();
+    }
+
+    @VisibleForTesting
+    public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
+        this(context);
+        mIconPlaceholder = iconPlaceholder;
+        mTextView = textView;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconPlaceholder = findViewById(R.id.icon_placeholder);
+        mTextView = findViewById(R.id.text);
+    }
+
+    public void setEntry(NotificationData.Entry entry) {
+        if (entry != null) {
+            mShowingEntry = entry;
+            CharSequence text = entry.headsUpStatusBarText;
+            if (mPublicMode) {
+                text = entry.headsUpStatusBarTextPublic;
+            }
+            mTextView.setText(text);
+        } else {
+            mShowingEntry = null;
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        mIconPlaceholder.getLocationOnScreen(mTmpPosition);
+        int left = (int) (mTmpPosition[0] - getTranslationX());
+        int top = mTmpPosition[1];
+        int right = left + mIconPlaceholder.getWidth();
+        int bottom = top + mIconPlaceholder.getHeight();
+        mLayoutedIconRect.set(left, top, right, bottom);
+        updateDrawingRect();
+        int targetPadding = mAbsoluteStartPadding + mLeftInset;
+        if (left != targetPadding) {
+            int newPadding = targetPadding - left + getPaddingStart();
+            setPaddingRelative(newPadding, 0, mEndMargin, 0);
+        }
+        if (mFirstLayout) {
+            // we need to do the padding calculation in the first frame, so the layout specified
+            // our visibility to be INVISIBLE in the beginning. let's correct that and set it
+            // to GONE.
+            setVisibility(GONE);
+            mFirstLayout = false;
+        }
+    }
+
+    @Override
+    public void setTranslationX(float translationX) {
+        super.setTranslationX(translationX);
+        updateDrawingRect();
+    }
+
+    private void updateDrawingRect() {
+        float oldLeft = mIconDrawingRect.left;
+        mIconDrawingRect.set(mLayoutedIconRect);
+        mIconDrawingRect.offset((int) getTranslationX(), 0);
+        if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
+            mOnDrawingRectChangedListener.run();
+        }
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        mLeftInset = insets.left;
+        return super.fitSystemWindows(insets);
+    }
+
+    public NotificationData.Entry getShowingEntry() {
+        return mShowingEntry;
+    }
+
+    public Rect getIconDrawingRect() {
+        return mIconDrawingRect;
+    }
+
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+    }
+
+    public void setPublicMode(boolean publicMode) {
+        mPublicMode = publicMode;
+    }
+
+    public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
+        mOnDrawingRectChangedListener = onDrawingRectChangedListener;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 859dc39..795140e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -92,6 +92,7 @@
     private boolean mVisible;
 
     private boolean mPowerPluggedIn;
+    private boolean mPowerPluggedInWired;
     private boolean mPowerCharged;
     private int mChargingSpeed;
     private int mChargingWattage;
@@ -476,6 +477,7 @@
         pw.println("KeyguardIndicationController:");
         pw.println("  mTransientTextColor: " + Integer.toHexString(mTransientTextColor));
         pw.println("  mInitialTextColor: " + Integer.toHexString(mInitialTextColor));
+        pw.println("  mPowerPluggedInWired: " + mPowerPluggedInWired);
         pw.println("  mPowerPluggedIn: " + mPowerPluggedIn);
         pw.println("  mPowerCharged: " + mPowerCharged);
         pw.println("  mChargingSpeed: " + mChargingSpeed);
@@ -496,12 +498,13 @@
             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
             boolean wasPluggedIn = mPowerPluggedIn;
+            mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
             mPowerCharged = status.isCharged();
             mChargingWattage = status.maxChargingWattage;
             mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
             mBatteryLevel = status.level;
-            updateIndication(!wasPluggedIn && mPowerPluggedIn);
+            updateIndication(!wasPluggedIn && mPowerPluggedInWired);
             if (mDozing) {
                 if (!wasPluggedIn && mPowerPluggedIn) {
                     showTransientIndication(computePowerIndication());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 73c8795..f64b1bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -602,14 +602,15 @@
                     && (mIsHeadsUp || mHeadsUpAnimatingAway)
                     && !mContainingNotification.isOnKeyguard();
             if (transitioningBetweenHunAndExpanded || pinned) {
-                return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
+                return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
+                        getViewHeight(VISIBLE_TYPE_EXPANDED));
             }
         }
 
         // Size change of the expanded version
         if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
                 && mExpandedChild != null) {
-            return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
+            return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
         }
 
         int hint;
@@ -619,16 +620,17 @@
                 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
             hint = mAmbientSingleLineChild.getHeight();
         } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
-            hint = mHeadsUpChild.getHeight();
+            hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
         } else if (mExpandedChild != null) {
-            hint = mExpandedChild.getHeight();
+            hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
         } else {
-            hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_action_list_height);
+            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+                    + mContext.getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.notification_action_list_height);
         }
 
         if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
-            hint = Math.min(hint, mExpandedChild.getHeight());
+            hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
         }
         return hint;
     }
@@ -694,8 +696,8 @@
     }
 
     private float calculateTransformationAmount() {
-        int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
-        int endHeight = getViewForVisibleType(mVisibleType).getHeight();
+        int startHeight = getViewHeight(mTransformationStartVisibleType);
+        int endHeight = getViewHeight(mVisibleType);
         int progress = Math.abs(mContentHeight - startHeight);
         int totalDistance = Math.abs(endHeight - startHeight);
         if (totalDistance == 0) {
@@ -717,11 +719,23 @@
         if (mContainingNotification.isShowingAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (mExpandedChild != null) {
-            return mExpandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+            return getViewHeight(VISIBLE_TYPE_EXPANDED)
+                    + getExtraRemoteInputHeight(mExpandedRemoteInput);
         } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
-            return mHeadsUpChild.getHeight() + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+            return getViewHeight(VISIBLE_TYPE_HEADSUP)
+                    + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
         }
-        return mContractedChild.getHeight();
+        return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+    }
+
+    private int getViewHeight(int visibleType) {
+        View view = getViewForVisibleType(visibleType);
+        int height = view.getHeight();
+        NotificationViewWrapper viewWrapper = getWrapperForView(view);
+        if (viewWrapper != null) {
+            height += viewWrapper.getHeaderTranslation();
+        }
+        return height;
     }
 
     public int getMinHeight() {
@@ -732,7 +746,7 @@
         if (mContainingNotification.isShowingAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
-            return mContractedChild.getHeight();
+            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
         } else {
             return mSingleLineView.getHeight();
         }
@@ -1046,7 +1060,7 @@
 
     private int getVisualTypeForHeight(float viewHeight) {
         boolean noExpandedChild = mExpandedChild == null;
-        if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
+        if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
             return VISIBLE_TYPE_EXPANDED;
         }
         if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
@@ -1055,13 +1069,13 @@
 
         if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
                 && !mContainingNotification.isOnKeyguard()) {
-            if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
+            if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
                 return VISIBLE_TYPE_HEADSUP;
             } else {
                 return VISIBLE_TYPE_EXPANDED;
             }
         } else {
-            if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
+            if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
                     && (!mIsChildInGroup || isGroupExpanded()
                             || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
                 return VISIBLE_TYPE_CONTRACTED;
@@ -1616,19 +1630,19 @@
     }
 
     public int getExpandHeight() {
-        View expandedChild = mExpandedChild;
-        if (expandedChild == null) {
-            expandedChild = mContractedChild;
+        int viewType = VISIBLE_TYPE_EXPANDED;
+        if (mExpandedChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
         }
-        return expandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
     }
 
     public int getHeadsUpHeight() {
-        View headsUpChild = mHeadsUpChild;
-        if (headsUpChild == null) {
-            headsUpChild = mContractedChild;
+        int viewType = VISIBLE_TYPE_HEADSUP;
+        if (mHeadsUpChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
         }
-        return headsUpChild.getHeight()+ getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
     }
 
     public void setRemoteInputVisible(boolean remoteInputVisible) {
@@ -1641,4 +1655,16 @@
         clipChildren = clipChildren && !mRemoteInputVisible;
         super.setClipChildren(clipChildren);
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mContractedWrapper != null) {
+            mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mHeadsUpWrapper != null) {
+            mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mExpandedWrapper != null) {
+            mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 22a186f..775faee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -107,6 +107,8 @@
         public CharSequence remoteInputTextWhenReset;
         public long lastRemoteInputSent = NOT_LAUNCHED_YET;
         public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+        public CharSequence headsUpStatusBarText;
+        public CharSequence headsUpStatusBarTextPublic;
 
         public Entry(StatusBarNotification n) {
             this.key = n.getKey();
@@ -529,6 +531,14 @@
         return null;
     }
 
+    public boolean shouldHide(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isSuspended();
+        }
+        return false;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
@@ -618,6 +628,10 @@
             return true;
         }
 
+        if (shouldHide(sbn.getKey())) {
+            return true;
+        }
+
         if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
                 && mGroupManager.isChildInGroupWithSummary(sbn)) {
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 48828ab..7a7cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -332,6 +332,7 @@
         row.setOnExpandClickListener(mPresenter);
         row.setInflationCallback(this);
         row.setLongPressListener(getNotificationLongClicker());
+        mListContainer.bindRow(row);
         mRemoteInputManager.bindRow(row);
 
         // Get the app name.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 1127075..886d6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -75,7 +75,7 @@
                 if (shouldApply) {
                     // lets gray it out
                     int grey = view.getContext().getColor(
-                            com.android.internal.R.color.notification_icon_default_color);
+                            com.android.internal.R.color.notification_default_color_light);
                     imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
                 } else {
                     // lets reset it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
index 0c19ec0..af9a3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
@@ -188,4 +188,11 @@
     default void applyExpandAnimationParams(ExpandAnimationParameters params) {}
 
     default void setExpandingNotification(ExpandableNotificationRow row) {}
+
+    /**
+     * Bind a newly created row.
+     *
+     * @param row The notification to bind.
+     */
+    default void bindRow(ExpandableNotificationRow row) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index cc584e7e..ccabb79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -389,7 +389,7 @@
      * "public" (secure & locked) mode?
      */
     private boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
+        if (isCurrentProfile(userHandle)) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 4f09133..41c7559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -177,6 +177,9 @@
             mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
                     getFullyClosedTranslation());
             mShelfState.zTranslation = ambientState.getBaseZHeight();
+            if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) {
+                mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
+            }
             float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
                     / (getIntrinsicHeight() * 2);
             openedAmount = Math.min(1.0f, openedAmount);
@@ -252,7 +255,8 @@
             }
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             float notificationClipEnd;
-            boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight;
+            boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
+                    || row.isPinned();
             boolean isLastChild = child == lastChild;
             float rowTranslationY = row.getTranslationY();
             if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
@@ -343,7 +347,7 @@
         float maxTop = row.getTranslationY();
         StatusBarIconView icon = row.getEntry().expandedIcon;
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
-        if (shelfIconPosition < maxTop) {
+        if (shelfIconPosition < maxTop && !mAmbientState.isDark()) {
             int top = (int) (maxTop - shelfIconPosition);
             Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
             icon.setClipBounds(clipRect);
@@ -354,7 +358,7 @@
 
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = row.getEntry().expandedIcon;
-        boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+        boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
             ViewTreeObserver.OnPreDrawListener predrawListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 0f5e71e..3cf7741 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -105,7 +105,6 @@
 
     private final int mMobileSignalGroupEndPadding;
     private final int mMobileDataIconStartPadding;
-    private final int mWideTypeIconStartPadding;
     private final int mSecondaryTelephonyPadding;
     private final int mEndPadding;
     private final int mEndPaddingNothingVisible;
@@ -136,7 +135,6 @@
                 res.getDimensionPixelSize(R.dimen.mobile_signal_group_end_padding);
         mMobileDataIconStartPadding =
                 res.getDimensionPixelSize(R.dimen.mobile_data_icon_start_padding);
-        mWideTypeIconStartPadding = res.getDimensionPixelSize(R.dimen.wide_type_icon_start_padding);
         mSecondaryTelephonyPadding = res.getDimensionPixelSize(R.dimen.secondary_telephony_padding);
         mEndPadding = res.getDimensionPixelSize(R.dimen.signal_cluster_battery_padding);
         mEndPaddingNothingVisible = res.getDimensionPixelSize(
@@ -302,7 +300,6 @@
         state.mMobileTypeId = statusType;
         state.mMobileDescription = statusIcon.contentDescription;
         state.mMobileTypeDescription = typeContentDescription;
-        state.mIsMobileTypeIconWide = statusType != 0 && isWide;
         state.mRoaming = roaming;
         state.mActivityIn = activityIn && mActivityEnabled;
         state.mActivityOut = activityOut && mActivityEnabled;
@@ -607,16 +604,17 @@
         private int mMobileStrengthId = 0, mMobileTypeId = 0;
         private int mLastMobileStrengthId = -1;
         private int mLastMobileTypeId = -1;
-        private boolean mIsMobileTypeIconWide;
         private String mMobileDescription, mMobileTypeDescription;
 
         private ViewGroup mMobileGroup;
-        private ImageView mMobile, mMobileDark, mMobileType, mMobileRoaming;
+        private ImageView mMobile, mMobileType, mMobileRoaming;
+        private View mMobileRoamingSpace;
         public boolean mRoaming;
         private ImageView mMobileActivityIn;
         private ImageView mMobileActivityOut;
         public boolean mActivityIn;
         public boolean mActivityOut;
+        private SignalDrawable mMobileSignalDrawable;
 
         public PhoneState(int subId, Context context) {
             ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -628,23 +626,19 @@
         public void setViews(ViewGroup root) {
             mMobileGroup    = root;
             mMobile         = root.findViewById(R.id.mobile_signal);
-            mMobileDark     = root.findViewById(R.id.mobile_signal_dark);
             mMobileType     = root.findViewById(R.id.mobile_type);
             mMobileRoaming  = root.findViewById(R.id.mobile_roaming);
+            mMobileRoamingSpace  = root.findViewById(R.id.mobile_roaming_space);
             mMobileActivityIn = root.findViewById(R.id.mobile_in);
             mMobileActivityOut = root.findViewById(R.id.mobile_out);
-            // TODO: Remove the 2 instances because now the drawable can handle darkness.
-            mMobile.setImageDrawable(new SignalDrawable(mMobile.getContext()));
-            SignalDrawable drawable = new SignalDrawable(mMobileDark.getContext());
-            drawable.setDarkIntensity(1);
-            mMobileDark.setImageDrawable(drawable);
+            mMobileSignalDrawable = new SignalDrawable(mMobile.getContext());
+            mMobile.setImageDrawable(mMobileSignalDrawable);
         }
 
         public boolean apply(boolean isSecondaryIcon) {
             if (mMobileVisible && !mIsAirplaneMode) {
                 if (mLastMobileStrengthId != mMobileStrengthId) {
                     mMobile.getDrawable().setLevel(mMobileStrengthId);
-                    mMobileDark.getDrawable().setLevel(mMobileStrengthId);
                     mLastMobileStrengthId = mMobileStrengthId;
                 }
 
@@ -663,18 +657,14 @@
             // When this isn't next to wifi, give it some extra padding between the signals.
             mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
                     0, 0, 0);
-            mMobile.setPaddingRelative(
-                    mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
-                    0, 0, 0);
-            mMobileDark.setPaddingRelative(
-                    mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
-                    0, 0, 0);
+            mMobile.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0);
 
             if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
                         (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
 
             mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
             mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
+            mMobileRoamingSpace.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
             mMobileActivityIn.setVisibility(mActivityIn ? View.VISIBLE : View.GONE);
             mMobileActivityOut.setVisibility(mActivityOut ? View.VISIBLE : View.GONE);
 
@@ -689,9 +679,7 @@
         }
 
         public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
-            applyDarkIntensity(
-                    DarkIconDispatcher.getDarkIntensity(tintArea, mMobile, darkIntensity),
-                    mMobile, mMobileDark);
+            mMobileSignalDrawable.setDarkIntensity(darkIntensity);
             setTint(mMobileType, DarkIconDispatcher.getTint(tintArea, mMobileType, tint));
             setTint(mMobileRoaming, DarkIconDispatcher.getTint(tintArea, mMobileRoaming,
                     tint));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 0a7ee51..badc40d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -112,14 +113,16 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
         performVisibilityAnimation(false);
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
         // TODO: use delay and duration
         performVisibilityAnimation(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6cfd42f..603902a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,7 +27,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -43,7 +42,6 @@
 import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
-import android.view.View;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
@@ -144,6 +142,8 @@
     private ColorMatrixColorFilter mMatrixColorFilter;
     private boolean mIsInShelf;
     private Runnable mLayoutRunnable;
+    private boolean mDismissed;
+    private Runnable mOnDismissListener;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -165,7 +165,7 @@
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
         if (mNotification != null) {
             setDecorColor(getContext().getColor(
-                    com.android.internal.R.color.notification_icon_default_color));
+                    com.android.internal.R.color.notification_default_color_light));
         }
         reloadDimens();
     }
@@ -668,6 +668,19 @@
     }
 
     public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
+        setVisibleState(visibleState, animate, endRunnable, 0);
+    }
+
+    /**
+     * Set the visibleState of this view.
+     *
+     * @param visibleState The new state.
+     * @param animate Should we animate?
+     * @param endRunnable The runnable to run at the end.
+     * @param duration The duration of an animation or 0 if the default should be taken.
+     */
+    public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
+            long duration) {
         boolean runnableAdded = false;
         if (visibleState != mVisibleState) {
             mVisibleState = visibleState;
@@ -689,7 +702,8 @@
                     mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
                             currentAmount, targetAmount);
                     mIconAppearAnimator.setInterpolator(interpolator);
-                    mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
+                    mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+                            : duration);
                     mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
                         @Override
                         public void onAnimationEnd(Animator animation) {
@@ -711,8 +725,9 @@
                 if (targetAmount != currentAmount) {
                     mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
                             currentAmount, targetAmount);
-                    mDotAnimator.setInterpolator(interpolator);
-                    mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
+                    mDotAnimator.setInterpolator(interpolator);;
+                    mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+                            : duration);
                     final boolean runRunnable = !runnableAdded;
                     mDotAnimator.addListener(new AnimatorListenerAdapter() {
                         @Override
@@ -837,6 +852,21 @@
         mLayoutRunnable = runnable;
     }
 
+    public void setDismissed() {
+        mDismissed = true;
+        if (mOnDismissListener != null) {
+            mOnDismissListener.run();
+        }
+    }
+
+    public boolean isDismissed() {
+        return mDismissed;
+    }
+
+    public void setOnDismissListener(Runnable onDismissListener) {
+        mOnDismissListener = onDismissListener;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 80854ec..2b7949b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -76,7 +76,6 @@
      * notification.
      */
     private final Context mPackageContext;
-    private boolean mIsLowPriority;
 
     public MediaNotificationProcessor(Context context, Context packageContext) {
         this(context, packageContext, new ImageGradientColorizer());
@@ -146,10 +145,7 @@
                 int foregroundColor = selectForegroundColor(backgroundColor, palette);
                 builder.setColorPalette(backgroundColor, foregroundColor);
             } else {
-                int id = mIsLowPriority
-                        ? R.color.notification_material_background_low_priority_color
-                        : R.color.notification_material_background_color;
-                backgroundColor = mContext.getColor(id);
+                backgroundColor = mContext.getColor(R.color.notification_material_background_color);
             }
             Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
                     mContext.getResources().getConfiguration().getLayoutDirection() ==
@@ -307,8 +303,4 @@
     private boolean isWhite(float[] hslColor) {
         return hslColor[2] >= WHITE_MIN_LIGHTNESS;
     }
-
-    public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index dfcd5e6..78df77f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -52,6 +52,7 @@
 
     protected final ViewInvertHelper mInvertHelper;
     protected final ViewTransformationHelper mTransformationHelper;
+    private final int mTranslationForHeader;
 
     protected int mColor;
     private ImageView mIcon;
@@ -63,6 +64,7 @@
     private boolean mIsLowPriority;
     private boolean mTransformLowPriorityTitle;
     private boolean mShowExpandButtonAtEnd;
+    protected float mHeaderTranslation;
 
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         super(ctx, view, row);
@@ -99,6 +101,10 @@
         resolveHeaderViews();
         updateInvertHelper();
         addAppOpsOnClickListener(row);
+        mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - ctx.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.notification_content_margin_top);
     }
 
     @Override
@@ -116,9 +122,9 @@
         mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
-        mColor = resolveColor(mExpandButton);
         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
         mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+        mColor = mNotificationHeader.getOriginalIconColor();
         getDozer().setColor(mColor);
     }
 
@@ -126,16 +132,6 @@
         mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
     }
 
-    private int resolveColor(ImageView icon) {
-        if (icon != null && icon.getDrawable() != null) {
-            ColorFilter filter = icon.getDrawable().getColorFilter();
-            if (filter instanceof PorterDuffColorFilter) {
-                return ((PorterDuffColorFilter) filter).getColor();
-            }
-        }
-        return 0;
-    }
-
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
         super.onContentUpdated(row);
@@ -243,6 +239,19 @@
     }
 
     @Override
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        super.setHeaderVisibleAmount(headerVisibleAmount);
+        mNotificationHeader.setAlpha(headerVisibleAmount);
+        mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+        mView.setTranslationY(mHeaderTranslation);
+    }
+
+    @Override
+    public int getHeaderTranslation() {
+        return (int) mHeaderTranslation;
+    }
+
+    @Override
     public NotificationHeaderView getNotificationHeader() {
         return mNotificationHeader;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index f967118..0143d01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -179,6 +179,9 @@
                     : builder.makeAmbientNotification();
         }
         result.packageContext = packageContext;
+        result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+        result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+                true /* showingPublic */);
         return result;
     }
 
@@ -456,6 +459,8 @@
                 }
                 entry.cachedAmbientContentView = result.newAmbientView;
             }
+            entry.headsUpStatusBarText = result.headsUpStatusBarText;
+            entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
             if (endListener != null) {
                 endListener.onAsyncInflationFinished(row.getEntry());
             }
@@ -579,15 +584,9 @@
                         mSbn.getNotification());
                 Context packageContext = mSbn.getPackageContext(mContext);
                 Notification notification = mSbn.getNotification();
-                if (mIsLowPriority) {
-                    int backgroundColor = mContext.getColor(
-                            R.color.notification_material_background_low_priority_color);
-                    recoveredBuilder.setBackgroundColorHint(backgroundColor);
-                }
                 if (notification.isMediaNotification()) {
                     MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                             packageContext);
-                    processor.setIsLowPriority(mIsLowPriority);
                     processor.processNotification(notification, recoveredBuilder);
                 }
                 return createRemoteViews(mReInflateFlags,
@@ -665,6 +664,8 @@
         private View inflatedExpandedView;
         private View inflatedAmbientView;
         private View inflatedPublicView;
+        private CharSequence headsUpStatusBarText;
+        private CharSequence headsUpStatusBarTextPublic;
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index d463eae..28beb21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -278,7 +278,10 @@
         if (mActionsContainer != null) {
             // We should never push the actions higher than they are in the headsup view.
             int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
-            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight());
+
+            // We also need to compensate for any header translation, since we're always at the end.
+            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
+                    - getHeaderTranslation());
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 17eb4c1..873f088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -133,6 +133,10 @@
         return null;
     }
 
+    public int getHeaderTranslation() {
+        return 0;
+    }
+
     @Override
     public TransformState getCurrentState(int fadingView) {
         return null;
@@ -198,4 +202,7 @@
     public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
         return false;
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index fb3adf4..07b79a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -24,6 +25,7 @@
 import android.util.MathUtils;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -36,19 +38,35 @@
     private static final int MAX_DURATION = 60 * 1000;
     public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully";
 
+    private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
+    private static DozeParameters sInstance;
+
     private final Context mContext;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+    private final PowerManager mPowerManager;
 
-    private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
 
     private boolean mDozeAlwaysOn;
+    private boolean mControlScreenOffAnimation;
 
-    public DozeParameters(Context context) {
+    public static DozeParameters getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new DozeParameters(context);
+        }
+        return sInstance;
+    }
+
+    @VisibleForTesting
+    protected DozeParameters(Context context) {
         mContext = context;
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
         mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context);
 
+        mControlScreenOffAnimation = !getDisplayNeedsBlanking();
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+
         Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
     }
@@ -165,15 +183,21 @@
                 com.android.internal.R.bool.config_displayBlanksAfterDoze);
     }
 
-    /**
-     * Whether we can implement our own screen off animation or if we need
-     * to rely on DisplayPowerManager to dim the display.
-     *
-     * @return {@code true} if SystemUI can control the screen off animation.
-     */
-    public boolean getCanControlScreenOffAnimation() {
-        return !mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_dozeAfterScreenOff);
+    public boolean shouldControlScreenOff() {
+        return mControlScreenOffAnimation;
+    }
+
+    public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) {
+        if (mControlScreenOffAnimation == controlScreenOffAnimation) {
+            return;
+        }
+        mControlScreenOffAnimation = controlScreenOffAnimation;
+        getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation);
+    }
+
+    @VisibleForTesting
+    protected PowerManager getPowerManager() {
+        return mPowerManager;
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 1011383..afd64f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -78,9 +78,10 @@
         }
     };
 
-    public DozeScrimController(ScrimController scrimController, Context context) {
+    public DozeScrimController(ScrimController scrimController, Context context,
+            DozeParameters dozeParameters) {
         mScrimController = scrimController;
-        mDozeParameters = new DozeParameters(context);
+        mDozeParameters = dozeParameters;
     }
 
     public void setDozing(boolean dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
new file mode 100644
index 0000000..9ec5609
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Controls the appearance of heads up notifications in the icon area and the header itself.
+ */
+class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
+        DarkIconDispatcher.DarkReceiver {
+    public static final int CONTENT_FADE_DURATION = 110;
+    public static final int CONTENT_FADE_DELAY = 100;
+    private final NotificationIconAreaController mNotificationIconAreaController;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final NotificationStackScrollLayout mStackScroller;
+    private final HeadsUpStatusBarView mHeadsUpStatusBarView;
+    private final View mClockView;
+    private final DarkIconDispatcher mDarkIconDispatcher;
+    private final NotificationPanelView mPanelView;
+    private final Consumer<ExpandableNotificationRow>
+            mSetTrackingHeadsUp = this::setTrackingHeadsUp;
+    private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
+    private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
+    private float mExpandedHeight;
+    private boolean mIsExpanded;
+    private float mExpandFraction;
+    private ExpandableNotificationRow mTrackedChild;
+    private boolean mShown;
+    private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
+            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+                    -> updatePanelTranslation();
+
+    public HeadsUpAppearanceController(
+            NotificationIconAreaController notificationIconAreaController,
+            HeadsUpManagerPhone headsUpManager,
+            View statusbarView) {
+        this(notificationIconAreaController, headsUpManager,
+                statusbarView.findViewById(R.id.heads_up_status_bar_view),
+                statusbarView.findViewById(R.id.notification_stack_scroller),
+                statusbarView.findViewById(R.id.notification_panel),
+                statusbarView.findViewById(R.id.clock));
+    }
+
+    @VisibleForTesting
+    public HeadsUpAppearanceController(
+            NotificationIconAreaController notificationIconAreaController,
+            HeadsUpManagerPhone headsUpManager,
+            HeadsUpStatusBarView headsUpStatusBarView,
+            NotificationStackScrollLayout stackScroller,
+            NotificationPanelView panelView,
+            View clockView) {
+        mNotificationIconAreaController = notificationIconAreaController;
+        mHeadsUpManager = headsUpManager;
+        mHeadsUpManager.addListener(this);
+        mHeadsUpStatusBarView = headsUpStatusBarView;
+        headsUpStatusBarView.setOnDrawingRectChangedListener(
+                () -> updateIsolatedIconLocation(true /* requireUpdate */));
+        mStackScroller = stackScroller;
+        mPanelView = panelView;
+        panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+        panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
+        panelView.setHeadsUpAppearanceController(this);
+        mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
+        mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+        mClockView = clockView;
+        mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
+        mDarkIconDispatcher.addDarkReceiver(this);
+    }
+
+
+    public void destroy() {
+        mHeadsUpManager.removeListener(this);
+        mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+        mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+        mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
+        mPanelView.setHeadsUpAppearanceController(null);
+        mStackScroller.removeOnExpandedHeightListener(mSetExpandedHeight);
+        mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+        mDarkIconDispatcher.removeDarkReceiver(this);
+    }
+
+    private void updateIsolatedIconLocation(boolean requireStateUpdate) {
+        mNotificationIconAreaController.setIsolatedIconLocation(
+                mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+    }
+
+    @Override
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        updateTopEntry();
+        updateHeader(headsUp.getEntry());
+    }
+
+    public void updatePanelTranslation() {
+        float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX();
+        mHeadsUpStatusBarView.setTranslationX(newTranslation);
+    }
+
+    private void updateTopEntry() {
+        NotificationData.Entry newEntry = null;
+        if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+            newEntry = mHeadsUpManager.getTopEntry();
+        }
+        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+        mHeadsUpStatusBarView.setEntry(newEntry);
+        if (newEntry != previousEntry) {
+            boolean animateIsolation = false;
+            if (newEntry == null) {
+                // no heads up anymore, lets start the disappear animation
+
+                setShown(false);
+                animateIsolation = !mIsExpanded;
+            } else if (previousEntry == null) {
+                // We now have a headsUp and didn't have one before. Let's start the disappear
+                // animation
+                setShown(true);
+                animateIsolation = !mIsExpanded;
+            }
+            updateIsolatedIconLocation(false /* requireUpdate */);
+            mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
+                    : newEntry.icon, animateIsolation);
+        }
+    }
+
+    private void setShown(boolean isShown) {
+        if (mShown != isShown) {
+            mShown = isShown;
+            if (isShown) {
+                mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
+                CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */,
+                        CONTENT_FADE_DELAY /* delay */);
+                CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */,
+                        0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE));
+            } else {
+                CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */,
+                        CONTENT_FADE_DELAY /* delay */);
+                CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */,
+                        0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE));
+
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public boolean isShown() {
+        return mShown;
+    }
+
+    /**
+     * Should the headsup status bar view be visible right now? This may be different from isShown,
+     * since the headsUp manager might not have notified us yet of the state change.
+     *
+     * @return if the heads up status bar view should be shown
+     */
+    public boolean shouldBeVisible() {
+        return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        updateTopEntry();
+        updateHeader(headsUp.getEntry());
+    }
+
+    public void setExpandedHeight(float expandedHeight, float appearFraction) {
+        boolean changedHeight = expandedHeight != mExpandedHeight;
+        mExpandedHeight = expandedHeight;
+        mExpandFraction = appearFraction;
+        boolean isExpanded = expandedHeight > 0;
+        if (changedHeight) {
+            updateHeadsUpHeaders();
+        }
+        if (isExpanded != mIsExpanded) {
+            mIsExpanded = isExpanded;
+            updateTopEntry();
+        }
+    }
+
+    /**
+     * Set a headsUp to be tracked, meaning that it is currently being pulled down after being
+     * in a pinned state on the top. The expand animation is different in that case and we need
+     * to update the header constantly afterwards.
+     *
+     * @param trackedChild the tracked headsUp or null if it's not tracking anymore.
+     */
+    public void setTrackingHeadsUp(ExpandableNotificationRow trackedChild) {
+        ExpandableNotificationRow previousTracked = mTrackedChild;
+        mTrackedChild = trackedChild;
+        if (previousTracked != null) {
+            updateHeader(previousTracked.getEntry());
+        }
+    }
+
+    private void updateHeadsUpHeaders() {
+        mHeadsUpManager.getAllEntries().forEach(entry -> {
+            updateHeader(entry);
+        });
+    }
+
+    private void updateHeader(NotificationData.Entry entry) {
+        ExpandableNotificationRow row = entry.row;
+        float headerVisibleAmount = 1.0f;
+        if (row.isPinned() || row == mTrackedChild) {
+            headerVisibleAmount = mExpandFraction;
+        }
+        row.setHeaderVisibleAmount(headerVisibleAmount);
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+    }
+
+    public void setPublicMode(boolean publicMode) {
+        mHeadsUpStatusBarView.setPublicMode(publicMode);
+        updateTopEntry();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index d2cdc27..fa0a774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
@@ -52,12 +53,13 @@
     private static final boolean DEBUG = false;
 
     private final View mStatusBarWindowView;
-    private int mStatusBarHeight;
     private final NotificationGroupManager mGroupManager;
     private final StatusBar mBar;
     private final VisualStabilityManager mVisualStabilityManager;
-
     private boolean mReleaseOnExpandFinish;
+
+    private int mStatusBarHeight;
+    private int mHeadsUpInset;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
     private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
@@ -101,9 +103,7 @@
         mBar = bar;
         mVisualStabilityManager = visualStabilityManager;
 
-        Resources resources = mContext.getResources();
-        mStatusBarHeight = resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
+        initResources();
 
         addListener(new OnHeadsUpChangedListener() {
             @Override
@@ -114,6 +114,20 @@
         });
     }
 
+    private void initResources() {
+        Resources resources = mContext.getResources();
+        mStatusBarHeight = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initResources();
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  Public methods:
 
@@ -283,10 +297,10 @@
             topEntry.getLocationOnScreen(mTmpTwoArray);
             int minX = mTmpTwoArray[0];
             int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int maxY = topEntry.getIntrinsicHeight();
+            int height = topEntry.getIntrinsicHeight();
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, 0, maxX, maxY);
+            info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
         } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 2bfdefe..903b813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,6 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
@@ -102,10 +101,11 @@
                     mCollapseSnoozes = h < 0;
                     mInitialTouchX = x;
                     mInitialTouchY = y;
-                    int expandedHeight = mPickedChild.getActualHeight();
-                    mPanel.setPanelScrimMinFraction((float) expandedHeight
+                    int startHeight = (int) (mPickedChild.getActualHeight()
+                                                + mPickedChild.getTranslationY());
+                    mPanel.setPanelScrimMinFraction((float) startHeight
                             / mPanel.getMaxPanelHeight());
-                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+                    mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
                     mPanel.startExpandingFromPeek();
                     // This call needs to be after the expansion start otherwise we will get a
                     // flicker of one frame as it's not expanded yet.
@@ -135,7 +135,7 @@
     private void setTrackingHeadsUp(boolean tracking) {
         mTrackingHeadsUp = tracking;
         mHeadsUpManager.setTrackingHeadsUp(tracking);
-        mPanel.setTrackingHeadsUp(tracking);
+        mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null);
     }
 
     public void notifyFling(boolean collapse) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 2a1813f..d609ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -60,10 +60,6 @@
     private final FalsingManager mFalsingManager;
     private final DismissCallbackRegistry mDismissCallbackRegistry;
     private final Handler mHandler;
-    protected KeyguardHostView mKeyguardView;
-    protected ViewGroup mRoot;
-    private boolean mShowingSoon;
-    private int mBouncerPromptReason;
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -72,7 +68,14 @@
                 }
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
+
     private int mStatusBarHeight;
+    private float mExpansion;
+    protected KeyguardHostView mKeyguardView;
+    protected ViewGroup mRoot;
+    private boolean mShowingSoon;
+    private int mBouncerPromptReason;
+    private boolean mIsAnimatingAway;
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -252,6 +255,7 @@
             mKeyguardView.cancelDismissAction();
             mKeyguardView.cleanUp();
         }
+        mIsAnimatingAway = false;
         if (mRoot != null) {
             mRoot.setVisibility(View.INVISIBLE);
             if (destroyView) {
@@ -267,6 +271,7 @@
      * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
      */
     public void startPreHideAnimation(Runnable runnable) {
+        mIsAnimatingAway = true;
         if (mKeyguardView != null) {
             mKeyguardView.startDisappearAnimation(runnable);
         } else if (runnable != null) {
@@ -290,7 +295,16 @@
     }
 
     public boolean isShowing() {
-        return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE);
+        return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+                && mExpansion == 0;
+    }
+
+    /**
+     * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
+     *         hidden yet, {@code false} otherwise.
+     */
+    public boolean isAnimatingAway() {
+        return mIsAnimatingAway;
     }
 
     public void prepare() {
@@ -308,7 +322,8 @@
      * @see StatusBarKeyguardViewManager#onPanelExpansionChanged
      */
     public void setExpansion(float fraction) {
-        if (mKeyguardView != null) {
+        mExpansion = fraction;
+        if (mKeyguardView != null && !mIsAnimatingAway) {
             float alpha = MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
             mKeyguardView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
             mKeyguardView.setTranslationY(fraction * mKeyguardView.getHeight());
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 1bd5e33..19e8295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -193,7 +193,8 @@
         float clockYTarget = mCurrentlySecure ? mMinTopMargin : -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
-        final float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
+        float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
+        shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(shadeExpansion);
         final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
 
         return (int) MathUtils.lerp(clockY, 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 b493d7a..a39800d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -464,7 +464,10 @@
     private void onRotationSuggestionsDisabled() {
         // Immediately hide the rotate button and clear any planned removal
         setRotateSuggestionButtonState(false, true);
-        getView().removeCallbacks(mRemoveRotationProposal);
+
+        // This method can be called before view setup is done, ensure getView isn't null
+        final View v = getView();
+        if (v != null) v.removeCallbacks(mRemoveRotationProposal);
     }
 
     private void showAndLogRotationSuggestion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index cd000fe..a0df558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -73,6 +73,7 @@
     private int mTouchDownY;
     private boolean mDownOnRecents;
     private VelocityTracker mVelocityTracker;
+    private boolean mIsInScreenPinning;
 
     private boolean mDockWindowEnabled;
     private boolean mDockWindowTouchSlopExceeded;
@@ -105,6 +106,9 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mIsInScreenPinning = mNavigationBarView.inScreenPinning();
+        }
         if (!canHandleGestures()) {
             return false;
         }
@@ -269,7 +273,7 @@
     }
 
     private boolean canHandleGestures() {
-        return !mNavigationBarView.inScreenPinning() && !mStatusBar.isKeyguardShowing()
+        return !mIsInScreenPinning && !mStatusBar.isKeyguardShowing()
                 && mStatusBar.isPresenterFullyCollapsed();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index fcbd37c..8fb0620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -679,11 +679,12 @@
 
     public void updateStates() {
         updateSlippery();
+        reloadNavIcons();
         updateNavButtonIcons();
     }
 
     private void updateSlippery() {
-        setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded());
+        setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded());
     }
 
     private void setSlippery(boolean slippery) {
@@ -818,8 +819,6 @@
     public void onOverviewProxyConnectionChanged(boolean isConnected) {
         updateStates();
         setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
-        reloadNavIcons();
-        updateNavButtonIcons();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e1aed7a..9063dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -12,9 +12,11 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -31,6 +33,8 @@
  */
 public class NotificationIconAreaController implements DarkReceiver {
     private final NotificationColorUtil mNotificationColorUtil;
+    private final NotificationEntryManager mEntryManager;
+    private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
 
     private int mIconSize;
     private int mIconHPadding;
@@ -48,6 +52,7 @@
         mStatusBar = statusBar;
         mNotificationColorUtil = NotificationColorUtil.getInstance(context);
         mContext = context;
+        mEntryManager = Dependency.get(NotificationEntryManager.class);
 
         initializeNotificationAreaViews(context);
     }
@@ -129,8 +134,8 @@
     }
 
     protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
-            NotificationData notificationData, boolean showAmbient) {
-        if (notificationData.isAmbient(entry.key) && !showAmbient) {
+            boolean showAmbient, boolean hideDismissed) {
+        if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
         if (!StatusBar.isTopLevelChild(entry)) {
@@ -139,9 +144,13 @@
         if (entry.row.getVisibility() == View.GONE) {
             return false;
         }
+        if (entry.row.isDismissed() && hideDismissed) {
+            return false;
+        }
 
         // showAmbient == show in shade but not shelf
-        if (!showAmbient && notificationData.shouldSuppressStatusBar(entry.key)) {
+        if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
+                entry.key)) {
             return false;
         }
 
@@ -151,28 +160,30 @@
     /**
      * Updates the notifications with the given list of notifications to display.
      */
-    public void updateNotificationIcons(NotificationData notificationData) {
+    public void updateNotificationIcons() {
 
-        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
-                false /* showAmbient */);
-        updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
-                NotificationShelf.SHOW_AMBIENT_ICONS);
+        updateStatusBarIcons();
+        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */);
 
         applyNotificationIconsTint();
     }
 
+    private void updateStatusBarIcons() {
+        updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+                false /* showAmbient */, true /* hideDismissed */);
+    }
+
     /**
      * Updates the notification icons for a host layout. This will ensure that the notification
      * host layout will have the same icons like the ones in here.
-     *
-     * @param notificationData the notification data to look up which notifications are relevant
      * @param function A function to look up an icon view based on an entry
      * @param hostLayout which layout should be updated
      * @param showAmbient should ambient notification icons be shown
+     * @param hideDismissed should dismissed icons be hidden
      */
-    private void updateIconsForLayout(NotificationData notificationData,
-            Function<NotificationData.Entry, StatusBarIconView> function,
-            NotificationIconContainer hostLayout, boolean showAmbient) {
+    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -181,7 +192,7 @@
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
                 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
+                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) {
                     toShow.add(function.apply(ent));
                 }
             }
@@ -243,10 +254,13 @@
 
         final FrameLayout.LayoutParams params = generateIconLayoutParams();
         for (int i = 0; i < toShow.size(); i++) {
-            View v = toShow.get(i);
+            StatusBarIconView v = toShow.get(i);
             // The view might still be transiently added if it was just removed and added again
             hostLayout.removeTransientView(v);
             if (v.getParent() == null) {
+                if (hideDismissed) {
+                    v.setOnDismissListener(mUpdateStatusBarIcons);
+                }
                 hostLayout.addView(v, i, params);
             }
         }
@@ -296,4 +310,12 @@
         mNotificationIcons.setDark(dark, false, 0);
         mShelfIcons.setDark(dark, false, 0);
     }
+
+    public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+        mNotificationIcons.showIconIsolated(icon, animated);
+    }
+
+    public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
+        mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index b29ac90..5517434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -100,6 +100,33 @@
         }
     }.setDuration(200).setDelay(50);
 
+    /**
+     * The animation property used for all icons that were not isolated, when the isolation ends.
+     * This just fades the alpha and doesn't affect the movement and has a delay.
+     */
+    private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS
+            = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CONTENT_FADE_DURATION);
+
+    /**
+     * The animation property used for the icon when its isolation ends.
+     * This animates the translation back to the right position.
+     */
+    private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CONTENT_FADE_DURATION);
+
     public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
     public static final int MAX_STATIC_ICONS = 4;
     private static final int MAX_DOTS = 3;
@@ -127,6 +154,10 @@
     private float mVisualOverflowStart;
     // Keep track of overflow in range [0, 3]
     private int mNumDots;
+    private StatusBarIconView mIsolatedIcon;
+    private Rect mIsolatedIconLocation;
+    private int[] mAbsolutePosition = new int[2];
+    private View mIsolatedIconForAnimation;
 
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
@@ -196,13 +227,18 @@
                 mIconSize = child.getWidth();
             }
         }
+        getLocationOnScreen(mAbsolutePosition);
         if (mIsStaticLayout) {
-            resetViewStates();
-            calculateIconTranslations();
-            applyIconStates();
+            updateState();
         }
     }
 
+    private void updateState() {
+        resetViewStates();
+        calculateIconTranslations();
+        applyIconStates();
+    }
+
     public void applyIconStates() {
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
@@ -214,6 +250,7 @@
         mAddAnimationStartIndex = -1;
         mCannedAnimationStartIndex = -1;
         mDisallowNextAnimation = false;
+        mIsolatedIconForAnimation = null;
     }
 
     @Override
@@ -281,8 +318,10 @@
                 mIconStates.remove(child);
                 if (!isReplacingIcon) {
                     addTransientView(icon, 0);
+                    boolean isIsolatedIcon = child == mIsolatedIcon;
                     icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
-                            () -> removeTransientView(icon));
+                            () -> removeTransientView(icon),
+                            isIsolatedIcon ? CONTENT_FADE_DURATION : 0);
                 }
             }
         }
@@ -306,7 +345,7 @@
             View view = getChildAt(i);
             ViewState iconState = mIconStates.get(view);
             iconState.initFrom(view);
-            iconState.alpha = 1.0f;
+            iconState.alpha = mIsolatedIcon == null || view == mIsolatedIcon ? 1.0f : 0.0f;
             iconState.hidden = false;
         }
     }
@@ -402,6 +441,16 @@
                 iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
             }
         }
+        if (mIsolatedIcon != null) {
+            IconState iconState = mIconStates.get(mIsolatedIcon);
+            if (iconState != null) {
+                // Most of the time the icon isn't yet added when this is called but only happening
+                // later
+                iconState.xTranslation = mIsolatedIconLocation.left - mAbsolutePosition[0]
+                        - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f;
+                iconState.visibleState = StatusBarIconView.STATE_ICON;
+            }
+        }
     }
 
     private float getLayoutEnd() {
@@ -573,6 +622,21 @@
         mReplacingIcons = replacingIcons;
     }
 
+    public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+        if (animated) {
+            mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
+        }
+        mIsolatedIcon = icon;
+        updateState();
+    }
+
+    public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) {
+        mIsolatedIconLocation = isolatedIconLocation;
+        if (requireUpdate) {
+            updateState();
+        }
+    }
+
     public class IconState extends ViewState {
         public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
         public float iconAppearAmount = 1.0f;
@@ -646,6 +710,18 @@
                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                         animate = true;
                     }
+                    if (mIsolatedIconForAnimation != null) {
+                        if (view == mIsolatedIconForAnimation) {
+                            animationProperties = UNISOLATION_PROPERTY;
+                            animationProperties.setDelay(
+                                    mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0);
+                        } else {
+                            animationProperties = UNISOLATION_PROPERTY_OTHERS;
+                            animationProperties.setDelay(
+                                    mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0);
+                        }
+                        animate = true;
+                    }
                 }
                 icon.setVisibleState(visibleState, animationsAllowed);
                 icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2711d7a..64e205d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -74,7 +74,9 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener,
@@ -243,6 +245,10 @@
     private float mExpandOffset;
     private boolean mHideIconsDuringNotificationLaunch = true;
     private int mStackScrollerMeasuringPass;
+    private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
+            = new ArrayList<>();
+    private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -269,6 +275,7 @@
         mNotificationStackScroller.setOnHeightChangedListener(this);
         mNotificationStackScroller.setOverscrollTopChangedListener(this);
         mNotificationStackScroller.setOnEmptySpaceClickListener(this);
+        addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
         mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = getResources().getConfiguration().orientation;
@@ -1139,6 +1146,14 @@
         @Override
         public void run() {
             mKeyguardStatusViewAnimating = false;
+            mKeyguardStatusView.setVisibility(View.INVISIBLE);
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusViewAnimating = false;
             mKeyguardStatusView.setVisibility(View.GONE);
         }
     };
@@ -1227,16 +1242,17 @@
 
     private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
             boolean goingToFullShade) {
+        mKeyguardStatusView.animate().cancel();
+        mKeyguardStatusViewAnimating = false;
         if ((!keyguardFadingAway && mStatusBarState == StatusBarState.KEYGUARD
                 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
-            mKeyguardStatusView.animate().cancel();
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.animate()
                     .alpha(0f)
                     .setStartDelay(0)
                     .setDuration(160)
                     .setInterpolator(Interpolators.ALPHA_OUT)
-                    .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
+                    .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable);
             if (keyguardFadingAway) {
                 mKeyguardStatusView.animate()
                         .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
@@ -1245,7 +1261,6 @@
             }
         } else if (mStatusBarState == StatusBarState.SHADE_LOCKED
                 && statusBarState == StatusBarState.KEYGUARD) {
-            mKeyguardStatusView.animate().cancel();
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.setAlpha(0f);
@@ -1256,13 +1271,21 @@
                     .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
         } else if (statusBarState == StatusBarState.KEYGUARD) {
-            mKeyguardStatusView.animate().cancel();
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.VISIBLE);
-            mKeyguardStatusView.setAlpha(1f);
+            if (keyguardFadingAway) {
+                mKeyguardStatusViewAnimating = true;
+                mKeyguardStatusView.animate()
+                        .alpha(0)
+                        .translationYBy(-getHeight() * 0.05f)
+                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                        .setDuration(125)
+                        .setStartDelay(0)
+                        .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+                        .start();
+            } else {
+                mKeyguardStatusView.setVisibility(View.VISIBLE);
+                mKeyguardStatusView.setAlpha(1f);
+            }
         } else {
-            mKeyguardStatusView.animate().cancel();
-            mKeyguardStatusViewAnimating = false;
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardStatusView.setAlpha(1f);
         }
@@ -1780,11 +1803,19 @@
         mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
         mIsExpansionFromHeadsUp = false;
-        mNotificationStackScroller.setTrackingHeadsUp(false);
+        notifyListenersTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
         setPanelScrimMinFraction(0.0f);
     }
 
+    private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+        for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
+            Consumer<ExpandableNotificationRow> listener
+                    = mTrackingHeadsUpListeners.get(i);
+            listener.accept(pickedChild);
+        }
+    }
+
     private void setListening(boolean listening) {
         mKeyguardStatusBar.setListening(listening);
         if (mQs == null) return;
@@ -2197,14 +2228,6 @@
         return (1 - t) * start + t * end;
     }
 
-    public void setDozing(boolean dozing, boolean animate) {
-        if (dozing == mDozing) return;
-        mDozing = dozing;
-        if (mStatusBarState == StatusBarState.KEYGUARD) {
-            updateDozingVisibilities(animate);
-        }
-    }
-
     private void updateDozingVisibilities(boolean animate) {
         if (mDozing) {
             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
@@ -2337,6 +2360,14 @@
 
     @Override
     public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+
+        // When we're unpinning the notification via active edge they remain heads-upped,
+        // we need to make sure that an animation happens in this case, otherwise the notification
+        // will stick to the top without any interaction.
+        if (isFullyCollapsed() && headsUp.isHeadsUp()) {
+            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
+            headsUp.setHeadsUpIsVisible();
+        }
     }
 
     @Override
@@ -2351,9 +2382,9 @@
                 this);
     }
 
-    public void setTrackingHeadsUp(boolean tracking) {
-        if (tracking) {
-            mNotificationStackScroller.setTrackingHeadsUp(true);
+    public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+        if (pickedChild != null) {
+            notifyListenersTrackingHeadsUp(pickedChild);
             mExpandingFromHeadsUp = true;
         }
         // otherwise we update the state when the expansion is finished
@@ -2400,6 +2431,10 @@
     protected void setVerticalPanelTranslation(float translation) {
         mNotificationStackScroller.setTranslationX(translation);
         mQsFrame.setTranslationX(translation);
+        int size = mVerticalTranslationListener.size();
+        for (int i = 0; i < size; i++) {
+            mVerticalTranslationListener.get(i).run();
+        }
     }
 
     protected void updateExpandedHeight(float expandedHeight) {
@@ -2555,6 +2590,10 @@
         if (mLaunchingNotification) {
             return mHideIconsDuringNotificationLaunch;
         }
+        if (mHeadsUpAppearanceController != null
+                && mHeadsUpAppearanceController.shouldBeVisible()) {
+            return false;
+        }
         return !isFullWidth() || !mShowIconsWhenExpanded;
     }
 
@@ -2600,11 +2639,16 @@
         }
     }
 
-    public void setDark(boolean dark, boolean animate) {
-        float darkAmount = dark ? 1 : 0;
-        if (mDarkAmount == darkAmount) {
-            return;
+    public void setDozing(boolean dozing, boolean animate) {
+        if (dozing == mDozing) return;
+        mDozing = dozing;
+
+        if (mStatusBarState == StatusBarState.KEYGUARD
+                || mStatusBarState == StatusBarState.SHADE_LOCKED) {
+            updateDozingVisibilities(animate);
         }
+
+        final float darkAmount = dozing ? 1 : 0;
         if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
             if (animate && mDarkAmountTarget == darkAmount) {
                 return;
@@ -2696,4 +2740,34 @@
             }
         }
     }
+
+    public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+        mTrackingHeadsUpListeners.add(listener);
+    }
+
+    public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+        mTrackingHeadsUpListeners.remove(listener);
+    }
+
+    public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
+        mVerticalTranslationListener.add(verticalTranslationListener);
+    }
+
+    public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
+        mVerticalTranslationListener.remove(verticalTranslationListener);
+    }
+
+    public void setHeadsUpAppearanceController(
+            HeadsUpAppearanceController headsUpAppearanceController) {
+        mHeadsUpAppearanceController = headsUpAppearanceController;
+    }
+
+    /**
+     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+     * security view of the bouncer.
+     */
+    public void onBouncerPreHideAnimation() {
+        setKeyguardStatusViewVisibility(mStatusBarState, true /* keyguardFadingAway */,
+                false /* goingToFullShade */);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index b448967..c4d7e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -28,6 +28,7 @@
     public static final String TAG = PanelBar.class.getSimpleName();
     private static final boolean SPEW = false;
     private boolean mBouncerShowing;
+    private boolean mExpanded;
 
     public static final void LOG(String fmt, Object... args) {
         if (!DEBUG) return;
@@ -71,10 +72,15 @@
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 
         setImportantForAccessibility(important);
+        updateVisibility();
 
         if (mPanel != null) mPanel.setImportantForAccessibility(important);
     }
 
+    private void updateVisibility() {
+        mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+    }
+
     public boolean panelEnabled() {
         return true;
     }
@@ -124,7 +130,8 @@
         boolean fullyOpened = false;
         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
         PanelView pv = mPanel;
-        pv.setVisibility(expanded || mBouncerShowing ? VISIBLE : INVISIBLE);
+        mExpanded = expanded;
+        updateVisibility();
         // adjust any other panels that may be partially visible
         if (expanded) {
             if (mState == STATE_CLOSED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 19544b1..9abbfb83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -352,7 +352,7 @@
 
     @Override
     public void onDraw(Canvas canvas) {
-        if (mNavigationBarView.isQuickScrubEnabled()) {
+        if (!mNavigationBarView.isQuickScrubEnabled()) {
             return;
         }
         int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index ae55ae8e..cfc0cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -49,7 +49,6 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.stack.ViewState;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -63,8 +62,8 @@
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
  */
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
-        OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
+        Dumpable {
 
     private static final String TAG = "ScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -106,7 +105,6 @@
     private final Context mContext;
     protected final ScrimView mScrimBehind;
     protected final ScrimView mScrimInFront;
-    private final View mHeadsUpScrim;
     private final LightBarController mLightBarController;
     private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -140,9 +138,6 @@
     private int mCurrentInFrontTint;
     private int mCurrentBehindTint;
     private boolean mWallpaperVisibilityTimedOut;
-    private int mPinnedHeadsUpCount;
-    private float mTopHeadsUpDragAmount;
-    private View mDraggedHeadsUpView;
     private int mScrimsVisibility;
     private final Consumer<Integer> mScrimVisibleListener;
     private boolean mBlankScreen;
@@ -158,13 +153,13 @@
 
     private final WakeLock mWakeLock;
     private boolean mWakeLockHeld;
+    private boolean mKeyguardOccluded;
 
     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
-            ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener,
+            ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener,
             DozeParameters dozeParameters, AlarmManager alarmManager) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
-        mHeadsUpScrim = headsUpScrim;
         mScrimVisibleListener = scrimVisibleListener;
         mContext = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
@@ -195,7 +190,6 @@
         }
         mState = ScrimState.UNINITIALIZED;
 
-        updateHeadsUpScrim(false);
         updateScrims();
     }
 
@@ -268,12 +262,13 @@
 
         // AOD wallpapers should fade away after a while
         if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
-                && (mState == ScrimState.AOD || mState == ScrimState.PULSING)) {
+                && mState == ScrimState.AOD) {
             if (!mWallpaperVisibilityTimedOut) {
                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
             }
-        } else {
+        // Do not re-schedule timeout when pulsing, let's save some extra battery.
+        } else if (mState != ScrimState.PULSING) {
             mTimeTicker.cancel();
             mWallpaperVisibilityTimedOut = false;
         }
@@ -283,10 +278,11 @@
             // with too many things at this case, in order to not skip the initial frames.
             mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
-        } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD) {
-            // Execute first frame immediately when display was completely off.
-            // Scheduling a frame isn't enough because the system may aggressively enter doze,
-            // delaying callbacks or never triggering them until the power button is pressed.
+        } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD
+                || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
+            // Scheduling a frame isn't enough when:
+            //  • Leaving doze and we need to modify scrim color immediately
+            //  • ColorFade will not kick-in and scrim cannot wait for pre-draw.
             onPreDraw();
         } else {
             scheduleUpdate();
@@ -317,7 +313,7 @@
 
     @VisibleForTesting
     protected void onHideWallpaperTimeout() {
-        if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
+        if (mState != ScrimState.AOD) {
             return;
         }
 
@@ -361,10 +357,6 @@
                 return;
             }
 
-            if (mPinnedHeadsUpCount != 0) {
-                updateHeadsUpScrim(false);
-            }
-
             setOrAdaptCurrentAnimation(mScrimBehind);
             setOrAdaptCurrentAnimation(mScrimInFront);
         }
@@ -478,11 +470,13 @@
             mLightBarController.setScrimColor(mScrimInFront.getColors());
         }
 
-        // We want to override the back scrim opacity for AOD and PULSING
+        // We want to override the back scrim opacity for the AOD state
         // when it's time to fade the wallpaper away.
-        boolean overrideBackScrimAlpha = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
-                && mWallpaperVisibilityTimedOut;
-        if (overrideBackScrimAlpha) {
+        boolean aodWallpaperTimeout = mState == ScrimState.AOD && mWallpaperVisibilityTimedOut;
+        // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
+        boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
+                && mKeyguardOccluded;
+        if (aodWallpaperTimeout || occludedKeyguard) {
             mCurrentBehindAlpha = 1;
         }
 
@@ -606,8 +600,6 @@
             return mCurrentInFrontAlpha;
         } else if (scrim == mScrimBehind) {
             return mCurrentBehindAlpha;
-        } else if (scrim == mHeadsUpScrim) {
-            return calculateHeadsUpAlpha();
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -618,8 +610,6 @@
             return mCurrentInFrontTint;
         } else if (scrim == mScrimBehind) {
             return mCurrentBehindTint;
-        } else if (scrim == mHeadsUpScrim) {
-            return Color.TRANSPARENT;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -666,40 +656,6 @@
         mScrimBehind.setDrawAsSrc(asSrc);
     }
 
-    @Override
-    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-    }
-
-    @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        mPinnedHeadsUpCount++;
-        updateHeadsUpScrim(true);
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        mPinnedHeadsUpCount--;
-        if (headsUp == mDraggedHeadsUpView) {
-            mDraggedHeadsUpView = null;
-            mTopHeadsUpDragAmount = 0.0f;
-        }
-        updateHeadsUpScrim(true);
-    }
-
-    @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-    }
-
-    private void updateHeadsUpScrim(boolean animate) {
-        if (animate) {
-            mAnimationDuration = ANIMATION_DURATION;
-            cancelAnimator((ValueAnimator) mHeadsUpScrim.getTag(TAG_KEY_ANIM));
-            startScrimAnimation(mHeadsUpScrim, mHeadsUpScrim.getAlpha());
-        } else {
-            setOrAdaptCurrentAnimation(mHeadsUpScrim);
-        }
-    }
-
     @VisibleForTesting
     void setOnAnimationFinished(Runnable onAnimationFinished) {
         mOnAnimationFinished = onAnimationFinished;
@@ -806,33 +762,6 @@
         return Handler.getMain();
     }
 
-    /**
-     * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
-     * the heads up is in its resting space and 1 means it's fully dragged out.
-     *
-     * @param draggedHeadsUpView the dragged view
-     * @param topHeadsUpDragAmount how far is it dragged
-     */
-    public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
-        mTopHeadsUpDragAmount = topHeadsUpDragAmount;
-        mDraggedHeadsUpView = draggedHeadsUpView;
-        updateHeadsUpScrim(false);
-    }
-
-    private float calculateHeadsUpAlpha() {
-        float alpha;
-        if (mPinnedHeadsUpCount >= 2) {
-            alpha = 1.0f;
-        } else if (mPinnedHeadsUpCount == 0) {
-            alpha = 0.0f;
-        } else {
-            alpha = 1.0f - mTopHeadsUpDragAmount;
-        }
-        float expandFactor = (1.0f - mExpansionFraction);
-        expandFactor = Math.max(expandFactor, 0.0f);
-        return alpha * expandFactor;
-    }
-
     public void setExcludedBackgroundArea(Rect area) {
         mScrimBehind.setExcludedArea(area);
     }
@@ -847,13 +776,6 @@
         mScrimBehind.setChangeRunnable(changeRunnable);
     }
 
-    public void onDensityOrFontScaleChanged() {
-        ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
-        layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
-                R.dimen.heads_up_scrim_height);
-        mHeadsUpScrim.setLayoutParams(layoutParams);
-    }
-
     public void setCurrentUser(int currentUser) {
         // Don't care in the base class.
     }
@@ -926,6 +848,10 @@
         mExpansionAffectsAlpha = expansionAffectsAlpha;
     }
 
+    public void setKeyguardOccluded(boolean keyguardOccluded) {
+        mKeyguardOccluded = keyguardOccluded;
+    }
+
     public interface Callback {
         default void onStart() {
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 55e8714..5b734eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -105,8 +105,7 @@
         @Override
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
-            final boolean wasPulsing = previousState == ScrimState.PULSING;
-            mBlankScreen = wasPulsing && !mCanControlScreenOff;
+            mBlankScreen = mDisplayRequiresBlanking;
             mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
                     && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
@@ -114,7 +113,7 @@
             mCurrentBehindTint = Color.BLACK;
             // DisplayPowerManager will blank the screen for us, we just need
             // to set our state.
-            mAnimateChange = mCanControlScreenOff;
+            mAnimateChange = !mDisplayRequiresBlanking;
         }
 
         @Override
@@ -178,7 +177,6 @@
     ScrimView mScrimBehind;
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
-    boolean mCanControlScreenOff;
     boolean mWallpaperSupportsAmbientMode;
     KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     int mIndex;
@@ -192,7 +190,6 @@
         mScrimBehind = scrimBehind;
         mDozeParameters = dozeParameters;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
-        mCanControlScreenOff = dozeParameters.getCanControlScreenOffAnimation();
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5bc45cd..e6a9b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
@@ -189,6 +190,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
@@ -208,6 +210,7 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -602,6 +605,9 @@
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
     protected ActivityLaunchAnimator mActivityLaunchAnimator;
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
+    private boolean mVibrateOnOpening;
+    private VibratorHelper mVibratorHelper;
 
     @Override
     public void start() {
@@ -639,6 +645,9 @@
         updateDisplaySize();
 
         Resources res = mContext.getResources();
+        mVibrateOnOpening = mContext.getResources().getBoolean(
+                R.bool.config_vibrateOnIconAnimation);
+        mVibratorHelper = Dependency.get(VibratorHelper.class);
         mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
         mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
 
@@ -747,8 +756,6 @@
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
         putComponent(DozeHost.class, mDozeServiceHost);
 
-        notifyUserAboutHiddenNotifications();
-
         mScreenPinningRequest = new ScreenPinningRequest(mContext);
         mFalsingManager = FalsingManager.getInstance(mContext);
 
@@ -809,6 +816,12 @@
                     mStatusBarView.setPanel(mNotificationPanel);
                     mStatusBarView.setScrimController(mScrimController);
                     mStatusBarView.setBouncerShowing(mBouncerShowing);
+                    if (mHeadsUpAppearanceController != null) {
+                        // This view is being recreated, let's destroy the old one
+                        mHeadsUpAppearanceController.destroy();
+                    }
+                    mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+                            mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
                     setAreThereNotifications();
                     checkBarModes();
                 }).getFragmentManager()
@@ -901,14 +914,14 @@
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
-        View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
         mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
-                scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
+                scrimBehind, scrimInFront, mLockscreenWallpaper,
                 scrimsVisible -> {
                     if (mStatusBarWindowManager != null) {
                         mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
                     }
-                }, new DozeParameters(mContext), mContext.getSystemService(AlarmManager.class));
+                }, DozeParameters.getInstance(mContext),
+                mContext.getSystemService(AlarmManager.class));
         if (mScrimSrcModeEnabled) {
             Runnable runnable = () -> {
                 boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
@@ -918,9 +931,9 @@
             mBackdrop.setOnVisibilityChangedRunnable(runnable);
             runnable.run();
         }
-        mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context);
+        mDozeScrimController = new DozeScrimController(mScrimController, context,
+                DozeParameters.getInstance(context));
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1075,7 +1088,6 @@
             mReinflateNotificationsOnUserSwitched = true;
         }
         // end old BaseStatusBar.onDensityOrFontScaleChanged().
-        mScrimController.onDensityOrFontScaleChanged();
         // TODO: Remove this.
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1089,6 +1101,7 @@
             mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
         }
         mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+        mHeadsUpManager.onDensityOrFontScaleChanged();
 
         reevaluateStyles();
     }
@@ -1386,8 +1399,7 @@
         updateQsExpansionEnabled();
 
         // Let's also update the icons
-        mNotificationIconAreaController.updateNotificationIcons(
-                mEntryManager.getNotificationData());
+        mNotificationIconAreaController.updateNotificationIcons();
     }
 
     @Override
@@ -1869,7 +1881,7 @@
 
     @Override
     public boolean isDozing() {
-        return mDozing;
+        return mDozing && mStackScroller.isFullyDark();
     }
 
     @Override
@@ -1993,7 +2005,7 @@
         mVisualStabilityManager.setPanelExpanded(isExpanded);
         if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
             if (DEBUG) {
-                Log.v(TAG, "clearing notification effects from setPanelExpanded");
+                Log.v(TAG, "clearing notification effects from setExpandedHeight");
             }
             clearNotificationEffects();
         }
@@ -2042,6 +2054,7 @@
 
     public void setOccluded(boolean occluded) {
         mIsOccluded = occluded;
+        mScrimController.setKeyguardOccluded(occluded);
         updateHideIconsForBouncer(false /* animate */);
     }
 
@@ -2151,6 +2164,9 @@
         } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
             if (mNotificationPanel.isFullyCollapsed()) {
+                if (mVibrateOnOpening) {
+                    mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+                }
                 mNotificationPanel.expand(true /* animate */);
                 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
             } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
@@ -2814,7 +2830,7 @@
                     public void setRemoteInputActive(NotificationData.Entry entry,
                             boolean remoteInputActive) {
                         mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                        entry.row.updateMaxHeights();
+                        entry.row.notifyHeightChanged(true /* needsAnimation */);
                     }
                     public void lockScrollTo(NotificationData.Entry entry) {
                         mStackScroller.lockScrollTo(entry.row);
@@ -3818,13 +3834,16 @@
     private void updateDozingState() {
         Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
         Trace.beginSection("StatusBar#updateDozingState");
+
+        boolean sleepingFromKeyguard =
+                mStatusBarKeyguardViewManager.isGoingToSleepVisibleNotOccluded();
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
-                || (mDozing && mDozeServiceHost.shouldAnimateScreenOff());
-        mNotificationPanel.setDozing(mDozing, animate);
+                || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
+
         mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
         mDozeScrimController.setDozing(mDozing);
         mKeyguardIndicationController.setDozing(mDozing);
-        mNotificationPanel.setDark(mDozing, animate);
+        mNotificationPanel.setDozing(mDozing, animate);
         updateQsExpansionEnabled();
         mViewHierarchyManager.updateRowStates();
         Trace.endSection();
@@ -3834,6 +3853,9 @@
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
         boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+        if (mHeadsUpAppearanceController != null) {
+            mHeadsUpAppearanceController.setPublicMode(publicMode);
+        }
         mStackScroller.setHideSensitive(publicMode, goingToFullShade);
         mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
@@ -4740,6 +4762,7 @@
             if (mDozingRequested) {
                 mDozingRequested = false;
                 DozeLog.traceDozing(mContext, mDozing);
+                mWakefulnessLifecycle.dispatchStartedWakingUp();
                 updateDozing();
             }
         }
@@ -5145,55 +5168,6 @@
 
     protected NotificationListener mNotificationListener;
 
-    protected void notifyUserAboutHiddenNotifications() {
-        if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
-            Log.d(TAG, "user hasn't seen notification about hidden notifications");
-            if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
-                Log.d(TAG, "insecure lockscreen, skipping notification");
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
-                return;
-            }
-            Log.d(TAG, "disabling lockscreen notifications and alerting the user");
-            // disable lockscreen notifications until user acts on the banner.
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
-
-            final String packageName = mContext.getPackageName();
-            PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
-                    new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
-            PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
-                    new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
-
-            final int colorRes = com.android.internal.R.color.system_notification_accent_color;
-            Notification.Builder note =
-                    new Notification.Builder(mContext, NotificationChannels.GENERAL)
-                            .setSmallIcon(R.drawable.ic_android)
-                            .setContentTitle(mContext.getString(
-                                    R.string.hidden_notifications_title))
-                            .setContentText(mContext.getString(R.string.hidden_notifications_text))
-                            .setOngoing(true)
-                            .setColor(mContext.getColor(colorRes))
-                            .setContentIntent(setupIntent)
-                            .addAction(R.drawable.ic_close,
-                                    mContext.getString(R.string.hidden_notifications_cancel),
-                                    cancelIntent)
-                            .addAction(R.drawable.ic_settings,
-                                    mContext.getString(R.string.hidden_notifications_setup),
-                                    setupIntent);
-            overrideNotificationAppName(mContext, note);
-
-            NotificationManager noMan =
-                    (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-            noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build());
-        }
-    }
-
     @Override  // NotificationData.Environment
     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
         final int notificationUserId = n.getUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b26b7c9..56a7b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -95,6 +95,7 @@
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
     private int mLastFpMode;
+    private boolean mGoingToSleepVisibleNotOccluded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
@@ -149,8 +150,7 @@
             mBouncer.setExpansion(expansion);
             if (expansion == 1) {
                 mBouncer.onFullyHidden();
-                updateStates();
-            } else if (!mBouncer.isShowing()) {
+            } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
                 mBouncer.show(true /* resetSecuritySelection */, false /* notifyFalsing */);
             } else if (noLongerTracking) {
                 // Notify that falsing manager should stop its session when user stops touching,
@@ -158,6 +158,9 @@
                 // data.
                 mBouncer.onFullyShown();
             }
+            if (expansion == 0 || expansion == 1) {
+                updateStates();
+            }
         }
         mLastTracking = tracking;
     }
@@ -262,11 +265,16 @@
         }
     }
 
+    public boolean isGoingToSleepVisibleNotOccluded() {
+        return mGoingToSleepVisibleNotOccluded;
+    }
+
     public void onStartedGoingToSleep() {
-        // TODO: remove
+        mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded();
     }
 
     public void onFinishedGoingToSleep() {
+        mGoingToSleepVisibleNotOccluded = false;
         mBouncer.onScreenTurnedOff();
     }
 
@@ -371,6 +379,7 @@
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (mBouncer.isShowing()) {
             mBouncer.startPreHideAnimation(finishRunnable);
+            mNotificationPanelView.onBouncerPreHideAnimation();
         } else if (finishRunnable != null) {
             finishRunnable.run();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index defb46c..309a1a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -73,7 +73,7 @@
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mActivityManager = ActivityManager.getService();
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
-        mDozeParameters = new DozeParameters(mContext);
+        mDozeParameters = DozeParameters.getInstance(mContext);
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
     }
 
@@ -141,11 +141,9 @@
             mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
         }
 
-        final boolean showWallpaperOnAod = mDozeParameters.getAlwaysOn() &&
-                state.wallpaperSupportsAmbientMode &&
-                state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_OPAQUE;
-        if (state.keyguardShowing && !state.backdropShowing &&
-                (!state.dozing || showWallpaperOnAod)) {
+        final boolean scrimsOccludingWallpaper =
+                state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
+        if (state.keyguardShowing && !state.backdropShowing && !scrimsOccludingWallpaper) {
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         } else {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 49f880c..7221efa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -93,7 +94,7 @@
 
     @Override
     public void setPowerSaveMode(boolean powerSave) {
-        mPowerManager.setPowerSaveMode(powerSave);
+        BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 040d7ec..aeda55a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -443,6 +443,9 @@
         entry.reset();
     }
 
+    public void onDensityOrFontScaleChanged() {
+    }
+
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 779b0fb..4533aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -198,7 +198,7 @@
             }
         });
         mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
-                mCallbackHandler, this);
+                mCallbackHandler, this, mWifiManager);
 
         mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index b22ce18..0adb439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -64,6 +64,7 @@
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+            .setUids(null)
             .build();
     private static final int NO_NETWORK = -1;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 7006d38..302f097 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -83,13 +83,8 @@
     private static final boolean DEBUG = false;
     private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
             "lockscreenSimpleUserSwitcher";
-    private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
-    private static final String ACTION_LOGOUT_USER = "com.android.systemui.LOGOUT_USER";
     private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
 
-    private static final String TAG_REMOVE_GUEST = "remove_guest";
-    private static final String TAG_LOGOUT_USER = "logout_user";
-
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     protected final Context mContext;
@@ -134,8 +129,6 @@
         mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
 
         filter = new IntentFilter();
-        filter.addAction(ACTION_REMOVE_GUEST);
-        filter.addAction(ACTION_LOGOUT_USER);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
                 PERMISSION_SELF, null /* scheduler */);
 
@@ -471,11 +464,6 @@
             if (mCallState == state) return;
             if (DEBUG) Log.v(TAG, "Call state changed: " + state);
             mCallState = state;
-            int currentUserId = ActivityManager.getCurrentUser();
-            UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
-            if (userInfo != null && userInfo.isGuest()) {
-                showGuestNotification(currentUserId);
-            }
             refreshUsers(UserHandle.USER_NULL);
         }
     };
@@ -491,16 +479,7 @@
             boolean unpauseRefreshUsers = false;
             int forcePictureLoadForId = UserHandle.USER_NULL;
 
-            if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
-                int currentUser = ActivityManager.getCurrentUser();
-                UserInfo userInfo = mUserManager.getUserInfo(currentUser);
-                if (userInfo != null && userInfo.isGuest()) {
-                    showExitGuestDialog(currentUser);
-                }
-                return;
-            } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
-                logoutCurrentUser();
-            } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
                 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
                     mExitGuestDialog.cancel();
                     mExitGuestDialog = null;
@@ -540,14 +519,6 @@
                             UserHandle.of(userInfo.id));
                     mSecondaryUser = userInfo.id;
                 }
-
-                if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
-                        && userInfo.id != UserHandle.USER_SYSTEM) {
-                    showLogoutNotification(currentId);
-                }
-                if (userInfo != null && userInfo.isGuest()) {
-                    showGuestNotification(currentId);
-                }
                 unpauseRefreshUsers = true;
             } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
                 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
@@ -564,52 +535,8 @@
                 mUnpauseRefreshUsers.run();
             }
         }
-
-        private void showLogoutNotification(int userId) {
-            PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext,
-                    0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
-            Notification.Builder builder =
-                    new Notification.Builder(mContext, NotificationChannels.GENERAL)
-                            .setVisibility(Notification.VISIBILITY_SECRET)
-                            .setSmallIcon(R.drawable.ic_person)
-                            .setContentTitle(mContext.getString(
-                                    R.string.user_logout_notification_title))
-                            .setContentText(mContext.getString(
-                                    R.string.user_logout_notification_text))
-                            .setContentIntent(logoutPI)
-                            .setOngoing(true)
-                            .setShowWhen(false)
-                            .addAction(R.drawable.ic_delete,
-                                    mContext.getString(R.string.user_logout_notification_action),
-                                    logoutPI);
-            SystemUI.overrideNotificationAppName(mContext, builder);
-            NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER,
-                    SystemMessage.NOTE_LOGOUT_USER, builder.build(), new UserHandle(userId));
-        }
     };
 
-    private void showGuestNotification(int guestUserId) {
-        boolean canSwitchUsers = mUserManager.canSwitchUsers();
-        // Disable 'Remove guest' action if cannot switch users right now
-        PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext,
-                0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null;
-
-        Notification.Builder builder =
-                new Notification.Builder(mContext, NotificationChannels.GENERAL)
-                        .setVisibility(Notification.VISIBILITY_SECRET)
-                        .setSmallIcon(R.drawable.ic_person)
-                        .setContentTitle(mContext.getString(R.string.guest_notification_title))
-                        .setContentText(mContext.getString(R.string.guest_notification_text))
-                        .setContentIntent(removeGuestPI)
-                        .setShowWhen(false)
-                        .addAction(R.drawable.ic_delete,
-                                mContext.getString(R.string.guest_notification_remove_action),
-                                removeGuestPI);
-        SystemUI.overrideNotificationAppName(mContext, builder);
-        NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST,
-                SystemMessage.NOTE_REMOVE_GUEST, builder.build(), new UserHandle(guestUserId));
-    }
-
     private final Runnable mUnpauseRefreshUsers = new Runnable() {
         @Override
         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b4c3eca..0f65421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -43,10 +43,11 @@
     private final WifiStatusTracker mWifiTracker;
 
     public WifiSignalController(Context context, boolean hasMobileData,
-            CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
+            CallbackHandler callbackHandler, NetworkControllerImpl networkController,
+            WifiManager wifiManager) {
         super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
                 callbackHandler, networkController);
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mWifiManager = wifiManager;
         mWifiTracker = new WifiStatusTracker(mWifiManager);
         mHasMobileData = hasMobileData;
         Handler handler = new WifiHandler(Looper.getMainLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 0f637fb..7c1c566 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -70,6 +70,8 @@
     private int mIntrinsicPadding;
     private int mExpandAnimationTopChange;
     private ExpandableNotificationRow mExpandingNotification;
+    private boolean mFullyDark;
+    private int mDarkTopPadding;
 
     public AmbientState(Context context) {
         reload(context);
@@ -409,4 +411,26 @@
     public int getExpandAnimationTopChange() {
         return mExpandAnimationTopChange;
     }
+
+    /**
+     * {@see isFullyDark}
+     */
+    public void setFullyDark(boolean fullyDark) {
+        mFullyDark = fullyDark;
+    }
+
+    /**
+     * @return {@code true } when shade is completely dark: in AOD or ambient display.
+     */
+    public boolean isFullyDark() {
+        return mFullyDark;
+    }
+
+    public void setDarkTopPadding(int darkTopPadding) {
+        mDarkTopPadding = darkTopPadding;
+    }
+
+    public int getDarkTopPadding() {
+        return mDarkTopPadding;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 53377d9..c26568e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -21,12 +21,12 @@
 import android.view.View;
 
 import java.util.ArrayList;
-import java.util.Set;
 
 /**
  * Filters the animations for only a certain type of properties.
  */
 public class AnimationFilter {
+    public static final int NO_DELAY = -1;
     boolean animateAlpha;
     boolean animateX;
     boolean animateY;
@@ -40,7 +40,7 @@
     public boolean animateShadowAlpha;
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
-    boolean hasHeadsUpDisappearClickEvent;
+    long customDelay;
     private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
 
     public AnimationFilter animateAlpha() {
@@ -129,8 +129,14 @@
                 hasGoToFullShadeEvent = true;
             }
             if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+            } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
                     .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
-                hasHeadsUpDisappearClickEvent = true;
+                // We need both timeouts when clicking, one to delay it and one for the animation
+                // to look nice
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
+                        + StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
             }
         }
     }
@@ -165,7 +171,7 @@
         animateHideSensitive = false;
         hasDelays = false;
         hasGoToFullShadeEvent = false;
-        hasHeadsUpDisappearClickEvent = false;
+        customDelay = NO_DELAY;
         mAnimatedProperties.clear();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
index 3bf7d89..a7925aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
@@ -233,7 +233,8 @@
         expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
 
         if (properties.wasAdded(child) && !hidden) {
-            expandableView.performAddAnimation(properties.delay, properties.duration);
+            expandableView.performAddAnimation(properties.delay, properties.duration,
+                    false /* isHeadsUpAppear */);
         }
 
         if (!expandableView.isInShelf() && this.inShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
index 05c0099..59ce0ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
@@ -23,6 +23,11 @@
  * An interpolator specifically designed for the appear animation of heads up notifications.
  */
 public class HeadsUpAppearInterpolator extends PathInterpolator {
+
+    private static float X1 = 250f;
+    private static float X2 = 200f;
+    private static float XTOT = (X1 + X2);;
+
     public HeadsUpAppearInterpolator() {
         super(getAppearPath());
     }
@@ -30,22 +35,18 @@
     private static Path getAppearPath() {
         Path path = new Path();
         path.moveTo(0, 0);
-        float x1 = 250f;
-        float x2 = 150f;
-        float x3 = 100f;
         float y1 = 90f;
-        float y2 = 78f;
-        float y3 = 80f;
-        float xTot = (x1 + x2 + x3);
-        path.cubicTo(x1 * 0.9f / xTot, 0f,
-                x1 * 0.8f / xTot, y1 / y3,
-                x1 / xTot , y1 / y3);
-        path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
-                (x1 + x2 * 0.2f) / xTot, y2 / y3,
-                (x1 + x2) / xTot, y2 / y3);
-        path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
-                (x1 + x2 + x3 * 0.2f) / xTot, 1f,
-                1f, 1f);
+        float y2 = 80f;
+        path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
+                X1 * 0.8f / XTOT, y1 / y2,
+                X1 / XTOT, y1 / y2);
+        path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
+                (X1 + X2 * 0.2f) / XTOT, 1.0f,
+                1.0f , 1.0f);
         return path;
     }
+
+    public static float getFractionUntilOvershoot() {
+        return X1 / XTOT;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index ac2a1e1..e5ab712 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -102,6 +102,9 @@
 
     private boolean mShowDividersWhenExpanded;
     private boolean mHideDividersDuringExpand;
+    private int mTranslationForHeader;
+    private int mCurrentHeaderTranslation = 0;
+    private float mHeaderVisibleAmount = 1.0f;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -142,6 +145,9 @@
                 res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
         mHideDividersDuringExpand =
                 res.getBoolean(R.bool.config_hideDividersDuringExpand);
+        mTranslationForHeader = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - mNotificationHeaderMargin;
     }
 
     @Override
@@ -486,7 +492,7 @@
         if (showingAsLowPriority()) {
             return mNotificationHeaderLowPriority.getHeight();
         }
-        int intrinsicHeight = mNotificationHeaderMargin;
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         boolean firstChild = true;
@@ -541,7 +547,7 @@
     public void getState(StackScrollState resultState, ExpandableViewState parentState,
             AmbientState ambientState) {
         int childCount = mChildren.size();
-        int yPosition = mNotificationHeaderMargin;
+        int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -645,6 +651,11 @@
             mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
                     ? parentState.zTranslation
                     : 0;
+            mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
+            mHeaderViewState.alpha = mHeaderVisibleAmount;
+            // The hiding is done automatically by the alpha, otherwise we'll pick it up again
+            // in the next frame with the initFrom call above and have an invisible header
+            mHeaderViewState.hidden = false;
         }
     }
 
@@ -1009,7 +1020,8 @@
             return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
                     /* likeHighPriority */);
         }
-        int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -1071,7 +1083,8 @@
     }
 
     private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1110,7 +1123,7 @@
         if (!likeHighPriority && showingAsLowPriority()) {
             return mNotificationHeaderLowPriority.getHeight();
         }
-        int minExpandHeight = mNotificationHeaderMargin;
+        int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mChildren.size();
@@ -1190,7 +1203,8 @@
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
-        int position = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
 
         for (int i = 0; i < mChildren.size(); i++) {
             ExpandableNotificationRow child = mChildren.get(i);
@@ -1281,4 +1295,9 @@
             last = false;
         }
     }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        mHeaderVisibleAmount = headerVisibleAmount;
+        mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
new file mode 100644
index 0000000..f98b3d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+import android.view.View;
+
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.HashSet;
+
+/**
+ * A class that manages the roundness for notification views
+ */
+class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+
+    private boolean mExpanded;
+    private ActivatableNotificationView mFirst;
+    private ActivatableNotificationView mLast;
+    private HashSet<View> mAnimatedChildren;
+    private Runnable mRoundingChangedCallback;
+    private ExpandableNotificationRow mTrackedHeadsUp;
+    private float mAppearFraction;
+
+    @Override
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, false /* animate */);
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, true /* animate */);
+    }
+
+    public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
+            boolean isAnimatingAway) {
+        updateRounding(row, false /* animate */);
+    }
+
+    private void updateRounding(ActivatableNotificationView view, boolean animate) {
+        float topRoundness = getRoundness(view, true /* top */);
+        float bottomRoundness = getRoundness(view, false /* top */);
+        boolean firstChanged = view.setTopRoundness(topRoundness, animate);
+        boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate);
+        if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) {
+            mRoundingChangedCallback.run();
+        }
+    }
+
+    private float getRoundness(ActivatableNotificationView view, boolean top) {
+        if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
+            return 1.0f;
+        }
+        if (view == mFirst && top) {
+            return 1.0f;
+        }
+        if (view == mLast && !top) {
+            return 1.0f;
+        }
+        if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+            // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
+            // rounded.
+            return 1.0f;
+        }
+        return 0.0f;
+    }
+
+    public void setExpanded(float expandedHeight, float appearFraction) {
+        mExpanded = expandedHeight != 0.0f;
+        mAppearFraction = appearFraction;
+        if (mTrackedHeadsUp != null) {
+            updateRounding(mTrackedHeadsUp, true);
+        }
+    }
+
+    public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
+            ActivatableNotificationView last) {
+        boolean firstChanged = mFirst != first;
+        boolean lastChanged = mLast != last;
+        if (!firstChanged && !lastChanged) {
+            return;
+        }
+        ActivatableNotificationView oldFirst = mFirst;
+        ActivatableNotificationView oldLast = mLast;
+        mFirst = first;
+        mLast = last;
+        if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) {
+            updateRounding(oldFirst, oldFirst.isShown());
+        }
+        if (lastChanged && oldLast != null && !oldLast.isRemoved()) {
+            updateRounding(oldLast, oldLast.isShown());
+        }
+        if (mFirst != null) {
+            updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst));
+        }
+        if (mLast != null) {
+            updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast));
+        }
+        mRoundingChangedCallback.run();
+    }
+
+    public void setAnimatedChildren(HashSet<View> animatedChildren) {
+        mAnimatedChildren = animatedChildren;
+    }
+
+    public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
+        mRoundingChangedCallback = roundingChangedCallback;
+    }
+
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackedHeadsUp = row;
+    }
+}
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 a85f4e2..dc94203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -94,8 +94,8 @@
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
@@ -108,6 +108,8 @@
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -289,6 +291,7 @@
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
     private HeadsUpManagerPhone mHeadsUpManager;
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -400,9 +403,11 @@
     private int mSidePaddings;
     private final int mSeparatorWidth;
     private final int mSeparatorThickness;
-    private final Rect mTmpRect = new Rect();
+    private final Rect mBackgroundAnimationRect = new Rect();
     private int mClockBottom;
     private int mAntiBurnInOffsetX;
+    private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
+    private int mHeadsUpInset;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -440,6 +445,9 @@
         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);
+        mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
+        mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
+        addOnExpandedHeightListener(mRoundnessManager::setExpanded);
 
         updateWillNotDraw();
         mBackgroundPaint.setAntiAlias(true);
@@ -515,26 +523,29 @@
         final int darkBottom = darkTop + mSeparatorThickness;
 
         if (mAmbientState.hasPulsingNotifications()) {
-            // TODO draw divider between notification and shelf
-        } else if (mAmbientState.isDark()) {
+            // No divider, we have a notification icon instead
+        } else if (mAmbientState.isFullyDark()) {
             // 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(1f - mDarkAmount);
             float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
                     .getInterpolation((1f - mDarkAmount) * 2);
-            mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
+            mBackgroundAnimationRect.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);
+            if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
+                canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
+                        mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
+                        mCornerRadius, mCornerRadius, mBackgroundPaint);
+            }
         }
+        updateClipping();
     }
 
     private void updateBackgroundDimming() {
@@ -582,13 +593,15 @@
                 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
         mMinTopOverScrollToEscape = res.getDimensionPixelSize(
                 R.dimen.min_top_overscroll_to_qs);
-        mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
         mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
         mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
         mMinInteractionHeight = res.getDimensionPixelSize(
                 R.dimen.notification_min_interaction_height);
         mCornerRadius = res.getDimensionPixelSize(
                 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
     }
 
     public void setDrawBackgroundAsSrc(boolean asSrc) {
@@ -693,7 +706,7 @@
         if (mPulsing) {
             mTopPadding = mClockBottom;
         } else {
-            mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
+            mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
         }
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
@@ -701,7 +714,8 @@
     }
 
     private void updateAlgorithmLayoutMinHeight() {
-        mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+        mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+                ? getLayoutMinHeight() : 0);
     }
 
     /**
@@ -820,6 +834,7 @@
         if (mRegularTopPadding != topPadding) {
             mRegularTopPadding = topPadding;
             mDarkTopPadding = topPadding + mDarkSeparatorPadding;
+            mAmbientState.setDarkTopPadding(mDarkTopPadding);
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -854,11 +869,12 @@
         float translationY;
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
+        float appearFraction = 1.0f;
         if (height >= appearEndPosition) {
             translationY = 0;
             stackHeight = (int) height;
         } else {
-            float appearFraction = getAppearFraction(height);
+            appearFraction = getAppearFraction(height);
             if (appearFraction >= 0) {
                 translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
                         appearFraction);
@@ -867,7 +883,12 @@
                 // start
                 translationY = height - appearStartPosition + getExpandTranslationStart();
             }
-            stackHeight = (int) (height - translationY);
+            if (isHeadsUpTransition()) {
+                stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
+                translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
+            } else {
+                stackHeight = (int) (height - translationY);
+            }
         }
         if (stackHeight != mCurrentStackHeight) {
             mCurrentStackHeight = stackHeight;
@@ -875,6 +896,10 @@
             requestChildrenUpdate();
         }
         setStackTranslation(translationY);
+        for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+            BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+            listener.accept(mExpandedHeight, appearFraction);
+        }
     }
 
     private void setRequestedClipBounds(Rect clipRect) {
@@ -883,13 +908,17 @@
     }
 
     public void updateClipping() {
+        boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
                 && !mHeadsUpAnimatingAway;
         if (mIsClipped != clipped) {
             mIsClipped = clipped;
             updateFadingState();
         }
-        if (clipped) {
+
+        if (animatingClipping) {
+            setClipBounds(mBackgroundAnimationRect);
+        } else if (clipped) {
             setClipBounds(mRequestedClipBounds);
         } else {
             setClipBounds(null);
@@ -909,12 +938,8 @@
      *         Measured in absolute height.
      */
     private float getAppearStartPosition() {
-        if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
-            if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
-                // If we ever expanded beyond the first notification, it's allowed to merge into
-                // the shelf
-                return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
-            }
+        if (isHeadsUpTransition()) {
+            return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
         }
         return getMinExpansionHeight();
     }
@@ -948,17 +973,14 @@
         int appearPosition;
         int notGoneChildCount = getNotGoneChildCount();
         if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
-            int minNotificationsForShelf = 1;
-            if (mTrackingHeadsUp
+            if (isHeadsUpTransition()
                     || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
                 appearPosition = getTopHeadsUpPinnedHeight();
-                minNotificationsForShelf = 2;
             } else {
                 appearPosition = 0;
-            }
-            if (notGoneChildCount >= minNotificationsForShelf
-                    && mShelf.getVisibility() != GONE) {
-                appearPosition += mShelf.getIntrinsicHeight();
+                if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
+                    appearPosition += mShelf.getIntrinsicHeight();
+                }
             }
         } else {
             appearPosition = mEmptyShadeView.getHeight();
@@ -966,6 +988,11 @@
         return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
     }
 
+    private boolean isHeadsUpTransition() {
+        return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
+                && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
+    }
+
     /**
      * @param height the height of the panel
      * @return the fraction of the appear animation that has been performed
@@ -1076,10 +1103,6 @@
 
     @Override
     public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
-        if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
-            mScrimController.setTopHeadsUpDragAmount(animView,
-                    Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
-        }
         // Returning true prevents alpha fading.
         return !mFadeNotificationsOnDismiss;
     }
@@ -2075,7 +2098,7 @@
         float previousPaddingAmount = 0.0f;
         int numShownItems = 0;
         boolean finish = false;
-        int maxDisplayedNotifications = mAmbientState.isDark()
+        int maxDisplayedNotifications = mAmbientState.isFullyDark()
                 ? (hasPulsingNotifications() ? 1 : 0)
                 : mMaxDisplayedNotifications;
 
@@ -2085,7 +2108,7 @@
                     && !expandableView.hasNoContentHeight()) {
                 boolean limitReached = maxDisplayedNotifications != -1
                         && numShownItems >= maxDisplayedNotifications;
-                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark()
+                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
                         && hasPulsingNotifications()
                         && expandableView instanceof ExpandableNotificationRow
                         && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
@@ -2168,7 +2191,7 @@
 
     private void updateBackground() {
         // No need to update the background color if it's not being drawn.
-        if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) {
+        if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
             return;
         }
 
@@ -2521,6 +2544,9 @@
     }
 
     public int getLayoutMinHeight() {
+        if (isHeadsUpTransition()) {
+            return getTopHeadsUpPinnedHeight();
+        }
         return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
     }
 
@@ -2929,42 +2955,18 @@
     private void updateFirstAndLastBackgroundViews() {
         ActivatableNotificationView firstChild = getFirstChildWithBackground();
         ActivatableNotificationView lastChild = getLastChildWithBackground();
-        boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
-        boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
         if (mAnimationsEnabled && mIsExpanded) {
-            mAnimateNextBackgroundTop = firstChanged;
-            mAnimateNextBackgroundBottom = lastChanged;
+            mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+            mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
         } else {
             mAnimateNextBackgroundTop = false;
             mAnimateNextBackgroundBottom = false;
         }
-        if (firstChanged && mFirstVisibleBackgroundChild != null
-                && !mFirstVisibleBackgroundChild.isRemoved()) {
-            mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
-                    mFirstVisibleBackgroundChild.isShown());
-        }
-        if (lastChanged && mLastVisibleBackgroundChild != null
-                && !mLastVisibleBackgroundChild.isRemoved()) {
-            mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
-                    mLastVisibleBackgroundChild.isShown());
-        }
         mFirstVisibleBackgroundChild = firstChild;
         mLastVisibleBackgroundChild = lastChild;
         mAmbientState.setLastVisibleBackgroundChild(lastChild);
-        applyRoundedNess();
-    }
-
-    private void applyRoundedNess() {
-        if (mFirstVisibleBackgroundChild != null) {
-            mFirstVisibleBackgroundChild.setTopRoundness(1.0f,
-                    mFirstVisibleBackgroundChild.isShown()
-                            && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
-        }
-        if (mLastVisibleBackgroundChild != null) {
-            mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
-                    mLastVisibleBackgroundChild.isShown()
-                            && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
-        }
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
+                mLastVisibleBackgroundChild);
         invalidate();
     }
 
@@ -3021,6 +3023,12 @@
     }
 
     @Override
+    public void bindRow(ExpandableNotificationRow row) {
+        row.setHeadsUpAnimatingAwayListener(animatingAway
+                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
+    }
+
+    @Override
     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
         mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
         requestChildrenUpdate();
@@ -3298,7 +3306,7 @@
                             .animateY(mShelf));
             ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
             mAnimationEvents.add(ev);
-            startBackgroundFadeIn();
+            startBackgroundFade();
         }
         mDarkNeedsAnimation = false;
     }
@@ -3889,7 +3897,6 @@
         requestChildrenUpdate();
         applyCurrentBackgroundBounds();
         updateWillNotDraw();
-        updateContentHeight();
         updateAntiBurnInTranslation();
         notifyHeightChangeListener(mShelf);
     }
@@ -3910,6 +3917,11 @@
 
     private void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
+        final boolean fullyDark = darkAmount == 1;
+        if (mAmbientState.isFullyDark() != fullyDark) {
+            mAmbientState.setFullyDark(fullyDark);
+            updateContentHeight();
+        }
         updateBackgroundDimming();
     }
 
@@ -3917,8 +3929,9 @@
         return mDarkAmount;
     }
 
-    private void startBackgroundFadeIn() {
-        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, 0f);
+    private void startBackgroundFade() {
+        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
+                mAmbientState.isDark() ? 1f : 0);
         fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
         fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
         fadeAnimator.start();
@@ -4288,6 +4301,7 @@
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
+        mHeadsUpManager.addListener(mRoundnessManager);
     }
 
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
@@ -4319,8 +4333,9 @@
         requestChildrenUpdate();
     }
 
-    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
-        mTrackingHeadsUp = trackingHeadsUp;
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackingHeadsUp = row != null;
+        mRoundnessManager.setTrackingHeadsUp(row);
     }
 
     public void setScrimController(ScrimController scrimController) {
@@ -4502,6 +4517,27 @@
                 mAmbientState.getScrollY()));
     }
 
+    public boolean isFullyDark() {
+        return mAmbientState.isFullyDark();
+    }
+
+    /**
+     * Add a listener whenever the expanded height changes. The first value passed as an argument
+     * is the expanded height and the second one is the appearFraction.
+     *
+     * @param listener the listener to notify.
+     */
+    public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+        mExpandedHeightListeners.add(listener);
+    }
+
+    /**
+     * Stop a listener from listening to the expandedHeight.
+     */
+    public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+        mExpandedHeightListeners.remove(listener);
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
@@ -4880,7 +4916,8 @@
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
-                        .animateZ(),
+                        .animateZ()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
                 new AnimationFilter()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 51737a8..7c8e0fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -50,6 +50,8 @@
     private boolean mIsExpanded;
     private boolean mClipNotificationScrollToTop;
     private int mStatusBarHeight;
+    private float mHeadsUpInset;
+    private int mPinnedZTranslationExtra;
 
     public StackScrollAlgorithm(Context context) {
         initView(context);
@@ -68,6 +70,10 @@
         mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
         mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
         mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+        mPinnedZTranslationExtra = res.getDimensionPixelSize(
+                R.dimen.heads_up_pinned_elevation);
     }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -184,7 +190,7 @@
     private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
             StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
-        boolean dark = ambientState.isDark();
+        boolean dark = ambientState.isFullyDark();
         boolean hideSensitive = ambientState.isHideSensitive();
         View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
@@ -457,7 +463,7 @@
                 }
             }
             if (row.isPinned()) {
-                childState.yTranslation = Math.max(childState.yTranslation, 0);
+                childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 childState.hidden = false;
                 ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
@@ -522,9 +528,6 @@
             childViewState.inShelf = true;
             childViewState.headsUpIsVisible = false;
         }
-        if (!ambientState.isShadeExpanded()) {
-            childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
-        }
     }
 
     protected int getMaxAllowedChildHeight(View child) {
@@ -592,6 +595,13 @@
         } else {
             childViewState.zTranslation = baseZ;
         }
+
+        // We need to scrim the notification more from its surrounding content when we are pinned,
+        // and we therefore elevate it higher.
+        // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
+        // expanding after which we have a normal elevation again.
+        childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
+                * mPinnedZTranslationExtra;
         return childrenOnTop;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 236c348..d48ae76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarIconView;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -45,13 +46,17 @@
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
     public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
-    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
-    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
+            = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
+                    * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
+    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
     public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
     public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
     public static final int ANIMATION_DELAY_HEADS_UP = 120;
+    public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
 
     private final int mGoToFullShadeAppearingTranslation;
     private final ExpandableViewState mTmpState = new ExpandableViewState();
@@ -74,8 +79,9 @@
     private ValueAnimator mBottomOverScrollAnimator;
     private int mHeadsUpAppearHeightBottom;
     private boolean mShadeExpanded;
-    private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
     private NotificationShelf mShelf;
+    private float mStatusBarIconLocation;
+    private int[] mTmpLocation = new int[2];
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
@@ -222,8 +228,8 @@
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
-        if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
-            return ANIMATION_DELAY_HEADS_UP;
+        if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
+            return mAnimationFilter.customDelay;
         }
         long minDelay = 0;
         for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
@@ -327,10 +333,6 @@
 
     private void onAnimationFinished() {
         mHostLayout.onChildAnimationFinished();
-        for (View v : mChildrenToClearFromOverlay) {
-            removeFromOverlay(v);
-        }
-        mChildrenToClearFromOverlay.clear();
     }
 
     /**
@@ -396,13 +398,14 @@
 
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        translationDirection, new Runnable() {
+                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
+                        0, new Runnable() {
                     @Override
                     public void run() {
                         // remove the temporary overlay
                         removeFromOverlay(changingView);
                     }
-                });
+                }, null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                 // A race condition can trigger the view to be added to the overlay even though
@@ -424,7 +427,9 @@
                 if (event.headsUpFromBottom) {
                     mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
                 } else {
-                    mTmpState.yTranslation = -mTmpState.height;
+                    mTmpState.yTranslation = 0;
+                    changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
+                            true /* isHeadsUpAppear */);
                 }
                 mHeadsUpAppearChildren.add(changingView);
                 mTmpState.applyToView(changingView);
@@ -433,22 +438,56 @@
                     event.animationType == NotificationStackScrollLayout
                             .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
                 mHeadsUpDisappearChildren.add(changingView);
+                Runnable endRunnable = null;
+                // We need some additional delay in case we were removed to make sure we're not
+                // lagging
+                int extraDelay = event.animationType == NotificationStackScrollLayout
+                        .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                        ? ANIMATION_DELAY_HEADS_UP_CLICKED
+                        : 0;
                 if (changingView.getParent() == null) {
                     // This notification was actually removed, so we need to add it to the overlay
                     mHostLayout.getOverlay().add(changingView);
                     mTmpState.initFrom(changingView);
-                    mTmpState.yTranslation = -changingView.getActualHeight();
+                    mTmpState.yTranslation = 0;
                     // We temporarily enable Y animations, the real filter will be combined
                     // afterwards anyway
                     mAnimationFilter.animateY = true;
-                    mAnimationProperties.delay =
-                            event.animationType == NotificationStackScrollLayout
-                                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                            ? ANIMATION_DELAY_HEADS_UP
-                            : 0;
+                    mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
                     mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
                     mTmpState.animateTo(changingView, mAnimationProperties);
-                    mChildrenToClearFromOverlay.add(changingView);
+                    endRunnable = () -> {
+                        // remove the temporary overlay
+                        removeFromOverlay(changingView);
+                    };
+                }
+                float targetLocation = 0;
+                boolean needsAnimation = true;
+                if (changingView instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+                    if (row.isDismissed()) {
+                        needsAnimation = false;
+                    }
+                    StatusBarIconView icon = row.getEntry().icon;
+                    if (icon.getParent() != null) {
+                        icon.getLocationOnScreen(mTmpLocation);
+                        float iconPosition = mTmpLocation[0] - icon.getTranslationX()
+                                + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+                        mHostLayout.getLocationOnScreen(mTmpLocation);
+                        targetLocation = iconPosition - mTmpLocation[0];
+                    }
+                }
+
+                if (needsAnimation) {
+                    // We need to add the global animation listener, since once no animations are
+                    // running anymore, the panel will instantly hide itself. We need to wait until
+                    // the animation is fully finished for this though.
+                    changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
+                                    + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
+                            true /* isHeadsUpAppear */, targetLocation, endRunnable,
+                            getGlobalAnimationFinishedListener());
+                } else if (endRunnable != null) {
+                    endRunnable.run();
                 }
             }
             mNewEvents.add(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd7..4b3643f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -641,6 +641,22 @@
     }
 
     /**
+     * Get the end value of the xTranslation animation running on a view or the xTranslation
+     * if no animation is running.
+     */
+    public static float getFinalTranslationX(View view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (xAnimator == null) {
+            return view.getTranslationX();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_X);
+        }
+    }
+
+    /**
      * Get the end value of the yTranslation animation running on a view or the yTranslation
      * if no animation is running.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index c3a53de..7ffca173 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -219,7 +219,7 @@
                                 .setCategory(Notification.CATEGORY_SYSTEM)
                                 .setDeleteIntent(buildSnoozeIntent(fsUuid))
                                 .extend(new Notification.TvExtender());
-                SystemUI.overrideNotificationAppName(mContext, builder);
+                SystemUI.overrideNotificationAppName(mContext, builder, false);
 
                 mNotificationManager.notifyAsUser(fsUuid, SystemMessage.NOTE_STORAGE_PRIVATE,
                         builder.build(), UserHandle.ALL);
@@ -247,7 +247,7 @@
                             .setLocalOnly(true)
                             .setCategory(Notification.CATEGORY_ERROR)
                             .extend(new Notification.TvExtender());
-            SystemUI.overrideNotificationAppName(mContext, builder);
+            SystemUI.overrideNotificationAppName(mContext, builder, false);
 
             mNotificationManager.notifyAsUser(disk.getId(), SystemMessage.NOTE_STORAGE_DISK,
                     builder.build(), UserHandle.ALL);
@@ -498,7 +498,7 @@
                         .setCategory(Notification.CATEGORY_PROGRESS)
                         .setProgress(100, status, false)
                         .setOngoing(true);
-        SystemUI.overrideNotificationAppName(mContext, builder);
+        SystemUI.overrideNotificationAppName(mContext, builder, false);
 
         mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
                 builder.build(), UserHandle.ALL);
@@ -548,7 +548,7 @@
                         .setLocalOnly(true)
                         .setCategory(Notification.CATEGORY_SYSTEM)
                         .setAutoCancel(true);
-        SystemUI.overrideNotificationAppName(mContext, builder);
+        SystemUI.overrideNotificationAppName(mContext, builder, false);
 
         mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
                 builder.build(), UserHandle.ALL);
@@ -582,7 +582,7 @@
                         .setVisibility(Notification.VISIBILITY_PUBLIC)
                         .setLocalOnly(true)
                         .extend(new Notification.TvExtender());
-        overrideNotificationAppName(mContext, builder);
+        overrideNotificationAppName(mContext, builder, false);
         return builder;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 14d5c6f5..fc932c3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -41,7 +41,7 @@
     @VisibleForTesting
     static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
-        NotificationChannel batteryChannel = new NotificationChannel(BATTERY,
+        final NotificationChannel batteryChannel = new NotificationChannel(BATTERY,
                 context.getString(R.string.notification_channel_battery),
                 NotificationManager.IMPORTANCE_MAX);
         final String soundPath = Settings.Global.getString(context.getContentResolver(),
@@ -50,22 +50,33 @@
                 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                 .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
                 .build());
+        batteryChannel.setBlockableSystem(true);
+        batteryChannel.setBypassDnd(true);
+
+        final NotificationChannel alerts = new NotificationChannel(
+                ALERTS,
+                context.getString(R.string.notification_channel_alerts),
+                NotificationManager.IMPORTANCE_HIGH);
+        alerts.setBypassDnd(true);
+
+        final NotificationChannel general = new NotificationChannel(
+                GENERAL,
+                context.getString(R.string.notification_channel_general),
+                NotificationManager.IMPORTANCE_MIN);
+        general.setBypassDnd(true);
+
+        final NotificationChannel storage = new NotificationChannel(
+                STORAGE,
+                context.getString(R.string.notification_channel_storage),
+                isTv(context)
+                        ? NotificationManager.IMPORTANCE_DEFAULT
+                        : NotificationManager.IMPORTANCE_LOW);
+        storage.setBypassDnd(true);
 
         nm.createNotificationChannels(Arrays.asList(
-                new NotificationChannel(
-                        ALERTS,
-                        context.getString(R.string.notification_channel_alerts),
-                        NotificationManager.IMPORTANCE_HIGH),
-                new NotificationChannel(
-                        GENERAL,
-                        context.getString(R.string.notification_channel_general),
-                        NotificationManager.IMPORTANCE_MIN),
-                new NotificationChannel(
-                        STORAGE,
-                        context.getString(R.string.notification_channel_storage),
-                        isTv(context)
-                                ? NotificationManager.IMPORTANCE_DEFAULT
-                                : NotificationManager.IMPORTANCE_LOW),
+                alerts,
+                general,
+                storage,
                 createScreenshotChannel(
                         context.getString(R.string.notification_channel_screenshot),
                         nm.getNotificationChannel(SCREENSHOTS_LEGACY)),
@@ -101,6 +112,8 @@
 
         screenshotChannel.setSound(Uri.parse(""), // silent
                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+        screenshotChannel.setBypassDnd(true);
+        screenshotChannel.setBlockableSystem(true);
 
         if (legacySS != null) {
             // Respect any user modified fields from the old channel.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
new file mode 100644
index 0000000..41b094a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings.Global;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.VolumeDialog;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.VolumeDialogController.State;
+import com.android.systemui.plugins.VolumeDialogController.StreamState;
+
+/**
+ * Car version of the volume dialog.
+ *
+ * A client of VolumeDialogControllerImpl and its state model.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class CarVolumeDialogImpl implements VolumeDialog {
+    private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+    private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
+    private static final int UPDATE_ANIMATION_DURATION = 80;
+
+    private final Context mContext;
+    private final H mHandler = new H();
+    private final VolumeDialogController mController;
+
+    private Window mWindow;
+    private CustomDialog mDialog;
+    private ViewGroup mDialogView;
+    private ViewGroup mDialogRowsView;
+    private final List<VolumeRow> mRows = new ArrayList<>();
+    private ConfigurableTexts mConfigurableTexts;
+    private final SparseBooleanArray mDynamic = new SparseBooleanArray();
+    private final KeyguardManager mKeyguard;
+    private final Object mSafetyWarningLock = new Object();
+    private final ColorStateList mActiveSliderTint;
+    private final ColorStateList mInactiveSliderTint;
+
+    private boolean mShowing;
+
+    private int mActiveStream;
+    private int mPrevActiveStream;
+    private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
+    private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
+    private State mState;
+    private SafetyWarningDialog mSafetyWarning;
+    private boolean mHovering = false;
+    private boolean mExpanded = false;
+    private View mExpandBtn;
+
+    public CarVolumeDialogImpl(Context context) {
+        mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+        mController = Dependency.get(VolumeDialogController.class);
+        mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
+        mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
+    }
+
+    public void init(int windowType, Callback callback) {
+        initDialog();
+
+        mController.addCallback(mControllerCallbackH, mHandler);
+        mController.getState();
+    }
+
+    @Override
+    public void destroy() {
+        mController.removeCallback(mControllerCallbackH);
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    private void initDialog() {
+        mDialog = new CustomDialog(mContext);
+
+        mConfigurableTexts = new ConfigurableTexts(mContext);
+        mHovering = false;
+        mShowing = false;
+        mWindow = mDialog.getWindow();
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+        mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+            | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+        mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+            | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+            | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+        final WindowManager.LayoutParams lp = mWindow.getAttributes();
+        lp.format = PixelFormat.TRANSLUCENT;
+        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+        lp.windowAnimations = -1;
+        mWindow.setAttributes(lp);
+        mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        mDialog.setCanceledOnTouchOutside(true);
+        mDialog.setContentView(R.layout.car_volume_dialog);
+        mDialog.setOnShowListener(dialog -> {
+            mDialogView.setTranslationY(-mDialogView.getHeight());
+            mDialogView.setAlpha(0);
+            mDialogView.animate()
+                .alpha(1)
+                .translationY(0)
+                .setDuration(300)
+                .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+                .start();
+        });
+        mExpandBtn = mDialog.findViewById(R.id.expand);
+        mExpandBtn.setOnClickListener(v -> {
+            mExpanded = !mExpanded;
+            updateRowsH(getActiveRow());
+        });
+        mDialogView = mDialog.findViewById(R.id.volume_dialog);
+        mDialogView.setOnHoverListener((v, event) -> {
+            int action = event.getActionMasked();
+            mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+                || (action == MotionEvent.ACTION_HOVER_MOVE);
+            rescheduleTimeoutH();
+            return true;
+        });
+
+        mDialogRowsView = mDialog.findViewById(R.id.car_volume_dialog_rows);
+
+        if (mRows.isEmpty()) {
+            addRow(AudioManager.STREAM_MUSIC,
+                R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
+            addRow(AudioManager.STREAM_RING,
+                R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
+            addRow(AudioManager.STREAM_ALARM,
+                R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false);
+        } else {
+            addExistingRows();
+        }
+
+        updateRowsH(getActiveRow());
+    }
+
+    private ColorStateList loadColorStateList(int colorResId) {
+        return ColorStateList.valueOf(mContext.getColor(colorResId));
+    }
+
+    public void setStreamImportant(int stream, boolean important) {
+        mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
+    }
+
+    public void setAutomute(boolean automute) {
+        if (mAutomute == automute) return;
+        mAutomute = automute;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    public void setSilentMode(boolean silentMode) {
+        if (mSilentMode == silentMode) return;
+        mSilentMode = silentMode;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
+        boolean defaultStream) {
+        addRow(stream, iconRes, iconMuteRes, important, defaultStream, false);
+    }
+
+    private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
+        boolean defaultStream, boolean dynamic) {
+        if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
+        VolumeRow row = new VolumeRow();
+        initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
+        mDialogRowsView.addView(row.view);
+        mRows.add(row);
+    }
+
+    private void addExistingRows() {
+        int N = mRows.size();
+        for (int i = 0; i < N; i++) {
+            final VolumeRow row = mRows.get(i);
+            initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important,
+                row.defaultStream);
+            mDialogRowsView.addView(row.view);
+            updateVolumeRowH(row);
+        }
+    }
+
+    private VolumeRow getActiveRow() {
+        for (VolumeRow row : mRows) {
+            if (row.stream == mActiveStream) {
+                return row;
+            }
+        }
+        return mRows.get(0);
+    }
+
+    private VolumeRow findRow(int stream) {
+        for (VolumeRow row : mRows) {
+            if (row.stream == stream) return row;
+        }
+        return null;
+    }
+
+    public void dump(PrintWriter writer) {
+        writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
+        writer.print("  mShowing: "); writer.println(mShowing);
+        writer.print("  mActiveStream: "); writer.println(mActiveStream);
+        writer.print("  mDynamic: "); writer.println(mDynamic);
+        writer.print("  mAutomute: "); writer.println(mAutomute);
+        writer.print("  mSilentMode: "); writer.println(mSilentMode);
+    }
+
+    private static int getImpliedLevel(SeekBar seekBar, int progress) {
+        final int m = seekBar.getMax();
+        final int n = m / 100 - 1;
+        final int level = progress == 0 ? 0
+            : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
+        return level;
+    }
+
+    @SuppressLint("InflateParams")
+    private void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes,
+        boolean important, boolean defaultStream) {
+        row.stream = stream;
+        row.iconRes = iconRes;
+        row.iconMuteRes = iconMuteRes;
+        row.important = important;
+        row.defaultStream = defaultStream;
+        row.view = mDialog.getLayoutInflater().inflate(R.layout.car_volume_dialog_row, null);
+        row.view.setId(row.stream);
+        row.view.setTag(row);
+        row.slider =  row.view.findViewById(R.id.volume_row_slider);
+        row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+        row.anim = null;
+
+        row.icon = row.view.findViewById(R.id.volume_row_icon);
+        row.icon.setImageResource(iconRes);
+    }
+
+    public void show(int reason) {
+        mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+    }
+
+    public void dismiss(int reason) {
+        mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+    }
+
+    private void showH(int reason) {
+        if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
+        mHandler.removeMessages(H.SHOW);
+        mHandler.removeMessages(H.DISMISS);
+        rescheduleTimeoutH();
+        if (mShowing) return;
+        mShowing = true;
+
+        mDialog.show();
+        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        mController.notifyVisible(true);
+    }
+
+    protected void rescheduleTimeoutH() {
+        mHandler.removeMessages(H.DISMISS);
+        final int timeout = computeTimeoutH();
+        mHandler.sendMessageDelayed(mHandler
+            .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+        if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+        mController.userActivity();
+    }
+
+    private int computeTimeoutH() {
+        if (mHovering) return 16000;
+        if (mSafetyWarning != null) return 5000;
+        return 3000;
+    }
+
+    protected void dismissH(int reason) {
+        mHandler.removeMessages(H.DISMISS);
+        mHandler.removeMessages(H.SHOW);
+        if (!mShowing) return;
+        mDialogView.animate().cancel();
+        mShowing = false;
+
+        mDialogView.setTranslationY(0);
+        mDialogView.setAlpha(1);
+        mDialogView.animate()
+            .alpha(0)
+            .translationY(-mDialogView.getHeight())
+            .setDuration(250)
+            .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+            .withEndAction(() -> mHandler.postDelayed(() -> {
+                if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
+                mDialog.dismiss();
+            }, 50))
+            .start();
+
+        Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+        mController.notifyVisible(false);
+        synchronized (mSafetyWarningLock) {
+            if (mSafetyWarning != null) {
+                if (D.BUG) Log.d(TAG, "SafetyWarning dismissed");
+                mSafetyWarning.dismiss();
+            }
+        }
+    }
+
+    private boolean shouldBeVisibleH(VolumeRow row) {
+        if (mExpanded) {
+            return true;
+        }
+        return row.defaultStream;
+    }
+
+    private void updateRowsH(final VolumeRow activeRow) {
+        if (D.BUG) Log.d(TAG, "updateRowsH");
+        if (!mShowing) {
+            trimObsoleteH();
+        }
+        // apply changes to all rows
+        for (final VolumeRow row : mRows) {
+            final boolean isActive = row == activeRow;
+            final boolean shouldBeVisible = shouldBeVisibleH(row);
+            Util.setVisOrGone(row.view, shouldBeVisible);
+            if (row.view.isShown()) {
+                updateVolumeRowSliderTintH(row, isActive);
+            }
+        }
+    }
+
+    private void trimObsoleteH() {
+        if (D.BUG) Log.d(TAG, "trimObsoleteH");
+        for (int i = mRows.size() - 1; i >= 0; i--) {
+            final VolumeRow row = mRows.get(i);
+            if (row.ss == null || !row.ss.dynamic) continue;
+            if (!mDynamic.get(row.stream)) {
+                mRows.remove(i);
+                mDialogRowsView.removeView(row.view);
+            }
+        }
+    }
+
+    protected void onStateChangedH(State state) {
+        mState = state;
+        mDynamic.clear();
+        // add any new dynamic rows
+        for (int i = 0; i < state.states.size(); i++) {
+            final int stream = state.states.keyAt(i);
+            final StreamState ss = state.states.valueAt(i);
+            if (!ss.dynamic) continue;
+            mDynamic.put(stream, true);
+            if (findRow(stream) == null) {
+                addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true,
+                    false, true);
+            }
+        }
+
+        if (mActiveStream != state.activeStream) {
+            mPrevActiveStream = mActiveStream;
+            mActiveStream = state.activeStream;
+            updateRowsH(getActiveRow());
+            rescheduleTimeoutH();
+        }
+        for (VolumeRow row : mRows) {
+            updateVolumeRowH(row);
+        }
+
+    }
+
+    private void updateVolumeRowH(VolumeRow row) {
+        if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
+        if (mState == null) return;
+        final StreamState ss = mState.states.get(row.stream);
+        if (ss == null) return;
+        row.ss = ss;
+        if (ss.level > 0) {
+            row.lastAudibleLevel = ss.level;
+        }
+        if (ss.level == row.requestedLevel) {
+            row.requestedLevel = -1;
+        }
+        final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
+        final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
+        final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
+        final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
+        final boolean isRingVibrate = isRingStream
+            && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
+        final boolean isRingSilent = isRingStream
+            && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
+        final boolean isZenPriorityOnly = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
+        final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+        final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
+            : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream)
+                : isZenPriorityOnly ? ((isAlarmStream && mState.disallowAlarms) ||
+                    (isMusicStream && mState.disallowMedia) ||
+                    (isRingStream && mState.disallowRinger) ||
+                    (isSystemStream && mState.disallowSystem))
+                    : false;
+
+        // update slider max
+        final int max = ss.levelMax * 100;
+        if (max != row.slider.getMax()) {
+            row.slider.setMax(max);
+        }
+
+        // update icon
+        final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
+        row.icon.setEnabled(iconEnabled);
+        row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
+        final int iconRes =
+            isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
+                : isRingSilent || zenMuted ? row.iconMuteRes
+                    : ss.routedToBluetooth ?
+                        (ss.muted ? R.drawable.ic_volume_media_bt_mute
+                            : R.drawable.ic_volume_media_bt)
+                        : mAutomute && ss.level == 0 ? row.iconMuteRes
+                            : (ss.muted ? row.iconMuteRes : row.iconRes);
+        row.icon.setImageResource(iconRes);
+        row.iconState =
+            iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
+                : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
+                    ? Events.ICON_STATE_MUTE
+                    : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
+                        ? Events.ICON_STATE_UNMUTE
+                        : Events.ICON_STATE_UNKNOWN;
+        if (iconEnabled) {
+            if (isRingStream) {
+                if (isRingVibrate) {
+                    row.icon.setContentDescription(mContext.getString(
+                        R.string.volume_stream_content_description_unmute,
+                        getStreamLabelH(ss)));
+                } else {
+                    if (mController.hasVibrator()) {
+                        row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_vibrate,
+                            getStreamLabelH(ss)));
+                    } else {
+                        row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_mute,
+                            getStreamLabelH(ss)));
+                    }
+                }
+            } else {
+                if (ss.muted || mAutomute && ss.level == 0) {
+                    row.icon.setContentDescription(mContext.getString(
+                        R.string.volume_stream_content_description_unmute,
+                        getStreamLabelH(ss)));
+                } else {
+                    row.icon.setContentDescription(mContext.getString(
+                        R.string.volume_stream_content_description_mute,
+                        getStreamLabelH(ss)));
+                }
+            }
+        } else {
+            row.icon.setContentDescription(getStreamLabelH(ss));
+        }
+
+        // ensure tracking is disabled if zenMuted
+        if (zenMuted) {
+            row.tracking = false;
+        }
+
+        // update slider
+        final boolean enableSlider = !zenMuted;
+        final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
+            : row.ss.level;
+        updateVolumeRowSliderH(row, enableSlider, vlevel);
+    }
+
+    private String getStreamLabelH(StreamState ss) {
+        if (ss.remoteLabel != null) {
+            return ss.remoteLabel;
+        }
+        try {
+            return mContext.getResources().getString(ss.name);
+        } catch (Resources.NotFoundException e) {
+            Slog.e(TAG, "Can't find translation for stream " + ss);
+            return "";
+        }
+    }
+
+    private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
+        if (isActive) {
+            row.slider.requestFocus();
+        }
+        final ColorStateList tint = isActive && row.slider.isEnabled() ? mActiveSliderTint
+            : mInactiveSliderTint;
+        if (tint == row.cachedSliderTint) return;
+        row.cachedSliderTint = tint;
+        row.slider.setProgressTintList(tint);
+        row.slider.setThumbTintList(tint);
+    }
+
+    private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel) {
+        row.slider.setEnabled(enable);
+        updateVolumeRowSliderTintH(row, row.stream == mActiveStream);
+        if (row.tracking) {
+            return;  // don't update if user is sliding
+        }
+        final int progress = row.slider.getProgress();
+        final int level = getImpliedLevel(row.slider, progress);
+        final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
+        final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
+            < USER_ATTEMPT_GRACE_PERIOD;
+        mHandler.removeMessages(H.RECHECK, row);
+        if (mShowing && rowVisible && inGracePeriod) {
+            if (D.BUG) Log.d(TAG, "inGracePeriod");
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(H.RECHECK, row),
+                row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
+            return;  // don't update if visible and in grace period
+        }
+        if (vlevel == level) {
+            if (mShowing && rowVisible) {
+                return;  // don't clamp if visible
+            }
+        }
+        final int newProgress = vlevel * 100;
+        if (progress != newProgress) {
+            if (mShowing && rowVisible) {
+                // animate!
+                if (row.anim != null && row.anim.isRunning()
+                    && row.animTargetProgress == newProgress) {
+                    return;  // already animating to the target progress
+                }
+                // start/update animation
+                if (row.anim == null) {
+                    row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
+                    row.anim.setInterpolator(new DecelerateInterpolator());
+                } else {
+                    row.anim.cancel();
+                    row.anim.setIntValues(progress, newProgress);
+                }
+                row.animTargetProgress = newProgress;
+                row.anim.setDuration(UPDATE_ANIMATION_DURATION);
+                row.anim.start();
+            } else {
+                // update slider directly to clamped value
+                if (row.anim != null) {
+                    row.anim.cancel();
+                }
+                row.slider.setProgress(newProgress, true);
+            }
+        }
+    }
+
+    private void recheckH(VolumeRow row) {
+        if (row == null) {
+            if (D.BUG) Log.d(TAG, "recheckH ALL");
+            trimObsoleteH();
+            for (VolumeRow r : mRows) {
+                updateVolumeRowH(r);
+            }
+        } else {
+            if (D.BUG) Log.d(TAG, "recheckH " + row.stream);
+            updateVolumeRowH(row);
+        }
+    }
+
+    private void setStreamImportantH(int stream, boolean important) {
+        for (VolumeRow row : mRows) {
+            if (row.stream == stream) {
+                row.important = important;
+                return;
+            }
+        }
+    }
+
+    private void showSafetyWarningH(int flags) {
+        if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
+            || mShowing) {
+            synchronized (mSafetyWarningLock) {
+                if (mSafetyWarning != null) {
+                    return;
+                }
+                mSafetyWarning = new SafetyWarningDialog(mContext, mController.getAudioManager()) {
+                    @Override
+                    protected void cleanUp() {
+                        synchronized (mSafetyWarningLock) {
+                            mSafetyWarning = null;
+                        }
+                        recheckH(null);
+                    }
+                };
+                mSafetyWarning.show();
+            }
+            recheckH(null);
+        }
+        rescheduleTimeoutH();
+    }
+
+    private final VolumeDialogController.Callbacks mControllerCallbackH
+        = new VolumeDialogController.Callbacks() {
+        @Override
+        public void onShowRequested(int reason) {
+            showH(reason);
+        }
+
+        @Override
+        public void onDismissRequested(int reason) {
+            dismissH(reason);
+        }
+
+        @Override
+        public void onScreenOff() {
+            dismissH(Events.DISMISS_REASON_SCREEN_OFF);
+        }
+
+        @Override
+        public void onStateChanged(State state) {
+            onStateChangedH(state);
+        }
+
+        @Override
+        public void onLayoutDirectionChanged(int layoutDirection) {
+            mDialogView.setLayoutDirection(layoutDirection);
+        }
+
+        @Override
+        public void onConfigurationChanged() {
+            mDialog.dismiss();
+            initDialog();
+            mConfigurableTexts.update();
+        }
+
+        @Override
+        public void onShowVibrateHint() {
+            if (mSilentMode) {
+                mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+            }
+        }
+
+        @Override
+        public void onShowSilentHint() {
+            if (mSilentMode) {
+                mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+            }
+        }
+
+        @Override
+        public void onShowSafetyWarning(int flags) {
+            showSafetyWarningH(flags);
+        }
+
+        @Override
+        public void onAccessibilityModeChanged(Boolean showA11yStream) {
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int SHOW = 1;
+        private static final int DISMISS = 2;
+        private static final int RECHECK = 3;
+        private static final int RECHECK_ALL = 4;
+        private static final int SET_STREAM_IMPORTANT = 5;
+        private static final int RESCHEDULE_TIMEOUT = 6;
+        private static final int STATE_CHANGED = 7;
+
+        public H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SHOW: showH(msg.arg1); break;
+                case DISMISS: dismissH(msg.arg1); break;
+                case RECHECK: recheckH((VolumeRow) msg.obj); break;
+                case RECHECK_ALL: recheckH(null); break;
+                case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
+                case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
+                case STATE_CHANGED: onStateChangedH(mState); break;
+            }
+        }
+    }
+
+    private final class CustomDialog extends Dialog implements DialogInterface {
+        public CustomDialog(Context context) {
+            super(context, com.android.systemui.R.style.qs_theme);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            rescheduleTimeoutH();
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected void onStart() {
+            super.setCanceledOnTouchOutside(true);
+            super.onStart();
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            mHandler.sendEmptyMessage(H.RECHECK_ALL);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (isShowing()) {
+                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                    dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+        private final VolumeRow mRow;
+
+        private VolumeSeekBarChangeListener(VolumeRow row) {
+            mRow = row;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (mRow.ss == null) return;
+            if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
+                + " onProgressChanged " + progress + " fromUser=" + fromUser);
+            if (!fromUser) return;
+            if (mRow.ss.levelMin > 0) {
+                final int minProgress = mRow.ss.levelMin * 100;
+                if (progress < minProgress) {
+                    seekBar.setProgress(minProgress);
+                    progress = minProgress;
+                }
+            }
+            final int userLevel = getImpliedLevel(seekBar, progress);
+            if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+                mRow.userAttempt = SystemClock.uptimeMillis();
+                if (mRow.requestedLevel != userLevel) {
+                    mController.setStreamVolume(mRow.stream, userLevel);
+                    mRow.requestedLevel = userLevel;
+                    Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
+                        userLevel);
+                }
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+            if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+            mController.setActiveStream(mRow.stream);
+            mRow.tracking = true;
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
+            mRow.tracking = false;
+            mRow.userAttempt = SystemClock.uptimeMillis();
+            final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+            Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
+            if (mRow.ss.level != userLevel) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
+                    USER_ATTEMPT_GRACE_PERIOD);
+            }
+        }
+    }
+
+    private static class VolumeRow {
+        private View view;
+        private ImageButton icon;
+        private SeekBar slider;
+        private int stream;
+        private StreamState ss;
+        private long userAttempt;  // last user-driven slider change
+        private boolean tracking;  // tracking slider touch
+        private int requestedLevel = -1;  // pending user-requested level via progress changed
+        private int iconRes;
+        private int iconMuteRes;
+        private boolean important;
+        private boolean defaultStream;
+        private ColorStateList cachedSliderTint;
+        private int iconState;  // from Events
+        private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
+        private int animTargetProgress;
+        private int lastAudibleLevel = 1;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 0203c43..6e5b548 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
@@ -80,6 +81,7 @@
         Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
                 .withDefault(this::createDefault)
+                .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
                 .withCallback(dialog -> {
                     if (mDialog != null) {
                         mDialog.destroy();
@@ -100,6 +102,14 @@
         return impl;
     }
 
+    private VolumeDialog createCarDefault() {
+        CarVolumeDialogImpl impl = new CarVolumeDialogImpl(mContext);
+        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
+        impl.setAutomute(true);
+        impl.setSilentMode(false);
+        return impl;
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (VOLUME_DOWN_SILENT.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e94d6bd..1c8a26c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -451,11 +451,11 @@
                 toastText = R.string.volume_dialog_ringer_guidance_ring;
                 break;
             case RINGER_MODE_SILENT:
-                toastText = R.string.volume_dialog_ringer_guidance_silent;
+                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
                 break;
             case RINGER_MODE_VIBRATE:
             default:
-                toastText = R.string.volume_dialog_ringer_guidance_vibrate;
+                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
         }
 
         Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index ebb088b..107ce1e 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -40,6 +40,7 @@
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     SystemUIPluginLib \
     SystemUISharedLib \
+    android-support-car \
     android-support-v4 \
     android-support-v7-recyclerview \
     android-support-v7-preference \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 8c4fd73..6683636 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -28,6 +28,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Looper;
@@ -37,6 +40,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.utils.os.FakeHandler;
 
 import org.junit.Before;
@@ -54,14 +58,17 @@
     FakeHandler mHandlerFake;
     @Mock
     DozeParameters mDozeParameters;
+    @Mock
+    WakeLock mWakeLock;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
         mServiceFake = new DozeServiceFake();
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
-        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters);
+        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
     }
 
     @Test
@@ -142,4 +149,23 @@
         assertFalse(mServiceFake.screenStateSet);
     }
 
+    @Test
+    public void test_holdsWakeLockWhenGoingToLowPowerDelayed() {
+        // Transition to low power mode will be delayed to let
+        // animations play at 60 fps.
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+        mHandlerFake.setMode(QUEUEING);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mHandlerFake.dispatchQueuedMessages();
+        reset(mWakeLock);
+
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        verify(mWakeLock).acquire();
+        verify(mWakeLock, never()).release();
+
+        mHandlerFake.dispatchQueuedMessages();
+        verify(mWakeLock).release();
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 75ade9d..0d8d952 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -28,15 +28,20 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.PowerManager;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.wakelock.WakeLockFake;
@@ -46,33 +51,39 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DozeUiTest extends SysuiTestCase {
 
+    @Mock
     private AlarmManager mAlarmManager;
+    @Mock
     private DozeMachine mMachine;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private DozeHost mHost;
     private WakeLockFake mWakeLock;
-    private DozeHostFake mHost;
     private Handler mHandler;
     private HandlerThread mHandlerThread;
     private DozeUi mDozeUi;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
         mHandlerThread = new HandlerThread("DozeUiTest");
         mHandlerThread.start();
-        mAlarmManager = mock(AlarmManager.class);
-        mMachine = mock(DozeMachine.class);
         mWakeLock = new WakeLockFake();
-        mHost = new DozeHostFake();
         mHandler = mHandlerThread.getThreadHandler();
-        DozeParameters params = mock(DozeParameters.class);
-        when(params.getCanControlScreenOffAnimation()).thenReturn(true);
-        when(params.getDisplayNeedsBlanking()).thenReturn(false);
 
-        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
+        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+                mDozeParameters, mKeyguardUpdateMonitor);
     }
 
     @After
@@ -96,18 +107,69 @@
     }
 
     @Test
-    public void propagatesAnimateScreenOff() {
-        Assert.assertTrue("animateScreenOff should be true", mHost.animateScreenOff);
+    public void propagatesAnimateScreenOff_noAlwaysOn() {
+        reset(mHost);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(false);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
 
-        DozeParameters params = mock(DozeParameters.class);
-        new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
-        Assert.assertFalse("animateScreenOff should be false", mHost.animateScreenOff);
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost).setAnimateScreenOff(eq(false));
     }
 
     @Test
-    public void transitionSetsAnimateWakeup() {
-        mHost.animateWakeup = false;
+    public void propagatesAnimateScreenOff_alwaysOn() {
+        reset(mHost);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        // Take over when the keyguard is visible.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        verify(mHost).setAnimateScreenOff(eq(true));
+
+        // Do not animate screen-off when keyguard isn't visible - PowerManager will do it.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost).setAnimateScreenOff(eq(false));
+    }
+
+    @Test
+    public void neverAnimateScreenOff_whenNotSupported() {
+        // Re-initialize DozeParameters saying that the display requires blanking.
+        reset(mDozeParameters);
+        reset(mHost);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+        mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+                mDozeParameters, mKeyguardUpdateMonitor);
+
+        // Never animate if display doesn't support it.
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mHost, never()).setAnimateScreenOff(eq(false));
+    }
+
+    @Test
+    public void transitionSetsAnimateWakeup_alwaysOn() {
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
         mDozeUi.transitionTo(UNINITIALIZED, DOZE);
-        Assert.assertTrue("animateScreenOff should be true", mHost.animateWakeup);
+        verify(mHost).setAnimateWakeup(eq(true));
+    }
+
+    @Test
+    public void keyguardVisibility_changesControlScreenOffAnimation() {
+        // Pre-condition
+        reset(mDozeParameters);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+        verify(mDozeParameters).setControlScreenOffAnimation(eq(false));
+        mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+        verify(mDozeParameters).setControlScreenOffAnimation(eq(true));
+    }
+
+    @Test
+    public void transitionSetsAnimateWakeup_noAlwaysOn() {
+        mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+        verify(mHost).setAnimateWakeup(eq(false));
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 2705bca..5c80bb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.IWallpaperManager;
-import android.os.Handler;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @RunWith(JUnit4.class)
@@ -77,7 +75,7 @@
     public void testAnimates_whenSupported() throws RemoteException {
         // Pre-conditions
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
-        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(true);
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
@@ -92,8 +90,8 @@
     public void testDoesNotAnimate_whenNotSupported() throws RemoteException {
         // Pre-conditions
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
-        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(false);
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index e15e0b4..9eba9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
@@ -99,6 +100,23 @@
     }
 
     @Test
+    public void doesNotDispatchTwice() throws Exception {
+        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchStartedWakingUp();
+        mWakefulness.dispatchFinishedWakingUp();
+        mWakefulness.dispatchFinishedWakingUp();
+        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchStartedGoingToSleep();
+        mWakefulness.dispatchFinishedGoingToSleep();
+        mWakefulness.dispatchFinishedGoingToSleep();
+
+        verify(mWakefulnessObserver, times(1)).onStartedGoingToSleep();
+        verify(mWakefulnessObserver, times(1)).onFinishedGoingToSleep();
+        verify(mWakefulnessObserver, times(1)).onStartedWakingUp();
+        verify(mWakefulnessObserver, times(1)).onFinishedWakingUp();
+    }
+
+    @Test
     public void dump() throws Exception {
         mWakefulness.dump(null, new PrintWriter(new ByteArrayOutputStream()), new String[0]);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
index e023e87..c3defa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.policy.Clock;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,6 +42,7 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
+@Ignore("Flaky")
 public class CarQsFragmentTest extends SysuiBaseFragmentTest {
     public CarQsFragmentTest() {
         super(CarQSFragment.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 34e444e..231a1866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -40,17 +40,22 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.function.Consumer;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
     private ExpandableNotificationRow mGroup;
     private NotificationTestHelper mNotificationTestHelper;
+    boolean mHeadsUpAnimatingAway = false;
 
     @Before
     public void setUp() throws Exception {
         mNotificationTestHelper = new NotificationTestHelper(mContext);
         mGroup = mNotificationTestHelper.createGroup();
+        mGroup.setHeadsUpAnimatingAwayListener(
+                animatingAway -> mHeadsUpAnimatingAway = animatingAway);
     }
 
     @Test
@@ -195,4 +200,12 @@
         mGroup.getAppOpsOnClickListener().onClick(view);
         verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
     }
+
+    @Test
+    public void testHeadsUpAnimatingAwayListener() {
+        mGroup.setHeadsUpAnimatingAway(true);
+        Assert.assertEquals(true, mHeadsUpAnimatingAway);
+        mGroup.setHeadsUpAnimatingAway(false);
+        Assert.assertEquals(false, mHeadsUpAnimatingAway);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index b1e1c02..2000bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -60,6 +60,7 @@
 
     private static final int UID_NORMAL = 123;
     private static final int UID_ALLOW_DURING_SETUP = 456;
+    private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
 
     private final StatusBarNotification mMockStatusBarNotification =
             mock(StatusBarNotification.class);
@@ -247,6 +248,22 @@
         assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
     }
 
+    @Test
+    public void testShouldFilterHiddenNotifications() {
+        // setup
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        // test should filter out hidden notifications:
+        // hidden
+        when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
+        assertTrue(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
+
+        // not hidden
+        when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
+        assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -269,6 +286,21 @@
         @Override
         protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
             super.getRanking(key, outRanking);
+            if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+            } else {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
+            }
             return true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 7e5db34..3703d6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -140,7 +140,7 @@
                     0,
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment);
+                    null, null, null, true, sentiment, false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index e89ff97..550a35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,11 +16,15 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.content.Context;
+import android.os.PowerManager;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher;
+
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -28,6 +32,14 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DozeParametersTest extends SysuiTestCase {
@@ -186,4 +198,38 @@
         }
     }
 
+    @Test
+    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+        TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
+        PowerManager mockedPowerManager = dozeParameters.getPowerManager();
+        dozeParameters.setControlScreenOffAnimation(true);
+        reset(mockedPowerManager);
+        dozeParameters.setControlScreenOffAnimation(false);
+        verify(mockedPowerManager).setDozeAfterScreenOff(eq(true));
+    }
+
+    @Test
+    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+        TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
+        PowerManager mockedPowerManager = dozeParameters.getPowerManager();
+        dozeParameters.setControlScreenOffAnimation(false);
+        reset(mockedPowerManager);
+        dozeParameters.setControlScreenOffAnimation(true);
+        verify(dozeParameters.getPowerManager()).setDozeAfterScreenOff(eq(false));
+    }
+
+    private class TestableDozeParameters extends DozeParameters {
+        private PowerManager mPowerManager;
+
+        TestableDozeParameters(Context context) {
+            super(context);
+            mPowerManager = mock(PowerManager.class);
+        }
+
+        @Override
+        protected PowerManager getPowerManager() {
+            return mPowerManager;
+        }
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index ca2f713..203ebe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -34,18 +34,23 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class DozeScrimControllerTest extends SysuiTestCase {
 
+    @Mock
     private ScrimController mScrimController;
+    @Mock
+    private DozeParameters mDozeParameters;
     private DozeScrimController mDozeScrimController;
 
     @Before
     public void setup() {
-        mScrimController = mock(ScrimController.class);
+        MockitoAnnotations.initMocks(this);
         // Make sure callbacks will be invoked to complete the lifecycle.
         doAnswer(invocationOnMock -> {
             ScrimController.Callback callback = invocationOnMock.getArgument(1);
@@ -56,7 +61,8 @@
         }).when(mScrimController).transitionTo(any(ScrimState.class),
                 any(ScrimController.Callback.class));
 
-        mDozeScrimController = new DozeScrimController(mScrimController, getContext());
+        mDozeScrimController = new DozeScrimController(mScrimController, getContext(),
+                mDozeParameters);
         mDozeScrimController.setDozing(true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
new file mode 100644
index 0000000..7a61bfe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
+
+    private final NotificationStackScrollLayout mStackScroller =
+            mock(NotificationStackScrollLayout.class);
+    private final NotificationPanelView mPanelView = mock(NotificationPanelView.class);
+    private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
+    private ExpandableNotificationRow mFirst;
+    private HeadsUpStatusBarView mHeadsUpStatusBarView;
+    private HeadsUpManagerPhone mHeadsUpManager;
+
+    @Before
+    public void setUp() throws Exception {
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+        mFirst = testHelper.createRow();
+        mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
+        mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
+                mock(TextView.class));
+        mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+        mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+                mock(NotificationIconAreaController.class),
+                mHeadsUpManager,
+                mHeadsUpStatusBarView,
+                mStackScroller,
+                mPanelView,
+                new View(mContext));
+        mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+    }
+
+    @Test
+    public void testShowinEntryUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
+    }
+
+    @Test
+    public void testShownUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertTrue(mHeadsUpAppearanceController.isShown());
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertFalse(mHeadsUpAppearanceController.isShown());
+    }
+
+    @Test
+    public void testHeaderUpdated() {
+        mFirst.setPinned(true);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+        when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
+
+        mFirst.setPinned(false);
+        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
+    }
+
+    @Test
+    public void testDestroy() {
+        reset(mHeadsUpManager);
+        reset(mDarkIconDispatcher);
+        reset(mPanelView);
+        reset(mStackScroller);
+        mHeadsUpAppearanceController.destroy();
+        verify(mHeadsUpManager).removeListener(any());
+        verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
+        verify(mPanelView).removeVerticalTranslationListener(any());
+        verify(mPanelView).removeTrackingHeadsUpListener(any());
+        verify(mPanelView).setHeadsUpAppearanceController(any());
+        verify(mStackScroller).removeOnExpandedHeightListener(any());
+        verify(mStackScroller).removeOnLayoutChangeListener(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index f3a8417..a37947d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -22,8 +22,6 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.calls;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
@@ -238,9 +236,19 @@
     }
 
     @Test
-    public void testIsShowing() {
+    public void testIsShowing_animated() {
         Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
-        mBouncer.show(true);
+        mBouncer.show(true /* reset */);
+        Assert.assertTrue("Should be showing", mBouncer.isShowing());
+    }
+
+    @Test
+    public void testIsShowing_forSwipeUp() {
+        mBouncer.setExpansion(1f);
+        mBouncer.show(true /* reset */, false /* animated */);
+        Assert.assertFalse("Should only be showing after collapsing notification panel",
+                mBouncer.isShowing());
+        mBouncer.setExpansion(0f);
         Assert.assertTrue("Should be showing", mBouncer.isShowing());
     }
 
@@ -269,6 +277,25 @@
     }
 
     @Test
+    public void testIsHiding_preHideOrHide() {
+        Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway());
+        mBouncer.startPreHideAnimation(null /* runnable */);
+        Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway());
+        mBouncer.hide(false /* destroyView */);
+        Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway());
+    }
+
+    @Test
+    public void testIsHiding_skipsTranslation() {
+        mBouncer.show(false /* reset */);
+        reset(mKeyguardHostView);
+        mBouncer.startPreHideAnimation(null /* runnable */);
+        mBouncer.setExpansion(0.5f);
+        verify(mKeyguardHostView, never()).setTranslationY(anyFloat());
+        verify(mKeyguardHostView, never()).setAlpha(anyFloat());
+    }
+
+    @Test
     public void testIsSecure() {
         Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index f088c0b..45845fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -64,7 +64,6 @@
     private SynchronousScrimController mScrimController;
     private ScrimView mScrimBehind;
     private ScrimView mScrimInFront;
-    private View mHeadsUpScrim;
     private Consumer<Integer> mScrimVisibilityCallback;
     private int mScrimVisibility;
     private LightBarController mLightBarController;
@@ -78,7 +77,6 @@
         mLightBarController = mock(LightBarController.class);
         mScrimBehind = new ScrimView(getContext());
         mScrimInFront = new ScrimView(getContext());
-        mHeadsUpScrim = new View(getContext());
         mWakeLock = mock(WakeLock.class);
         mAlarmManager = mock(AlarmManager.class);
         mAlwaysOnEnabled = true;
@@ -87,7 +85,7 @@
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
         mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
-                mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
+                mScrimInFront, mScrimVisibilityCallback, mDozeParamenters,
                 mAlarmManager);
     }
 
@@ -349,7 +347,7 @@
     }
 
     @Test
-    public void testWillHideAoDWallpaper() {
+    public void testWillHideAodWallpaper() {
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
@@ -415,56 +413,6 @@
     }
 
     @Test
-    public void testHeadsUpScrimOpacity() {
-        mScrimController.setPanelExpansion(0f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
-    public void testHeadsUpScrimCounting() {
-        mScrimController.setPanelExpansion(0f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should only disappear when counter reaches 0", 1f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.onHeadsUpUnPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-        Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
-    public void testNoHeadsUpScrimExpanded() {
-        mScrimController.setPanelExpansion(1f);
-        mScrimController.onHeadsUpPinned(null /* row */);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Heads-up scrim should not be visible when shade is expanded", 0f,
-                mHeadsUpScrim.getAlpha(), 0.01f);
-    }
-
-    @Test
     public void testScrimFocus() {
         mScrimController.transitionTo(ScrimState.AOD);
         Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
@@ -475,6 +423,19 @@
         Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
     }
 
+    @Test
+    public void testHidesShowWhenLockedActivity() {
+        mScrimController.setWallpaperSupportsAmbientMode(true);
+        mScrimController.setKeyguardOccluded(true);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+
+        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.finishAnimationsImmediately();
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+    }
+
     /**
      * Conserves old notification density after leaving state and coming back.
      *
@@ -527,16 +488,23 @@
 
         private FakeHandler mHandler;
         private boolean mAnimationCancelled;
+        boolean mOnPreDrawCalled;
 
         SynchronousScrimController(LightBarController lightBarController,
-                ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+                ScrimView scrimBehind, ScrimView scrimInFront,
                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
                 AlarmManager alarmManager) {
-            super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+            super(lightBarController, scrimBehind, scrimInFront,
                     scrimVisibleListener, dozeParameters, alarmManager);
             mHandler = new FakeHandler(Looper.myLooper());
         }
 
+        @Override
+        public boolean onPreDraw() {
+            mOnPreDrawCalled = true;
+            return super.onPreDraw();
+        }
+
         void finishAnimationsImmediately() {
             boolean[] animationFinished = {false};
             setOnAnimationFinished(()-> animationFinished[0] = true);
@@ -549,7 +517,6 @@
             // Force finish all animations.
             endAnimation(mScrimBehind, TAG_KEY_ANIM);
             endAnimation(mScrimInFront, TAG_KEY_ANIM);
-            endAnimation(mHeadsUpScrim, TAG_KEY_ANIM);
 
             if (!animationFinished[0]) {
                 throw new IllegalStateException("Animation never finished");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 14baaeb..f13fa4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -547,6 +548,16 @@
         verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
     }
 
+    @Test
+    public void testSetOccluded_propagatesToScrimController() {
+        mStatusBar.setOccluded(true);
+        verify(mScrimController).setKeyguardOccluded(eq(true));
+
+        reset(mScrimController);
+        mStatusBar.setOccluded(false);
+        verify(mScrimController).setKeyguardOccluded(eq(false));
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 30c7b53..fc3de84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -5,6 +5,7 @@
 import android.net.NetworkInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -21,6 +22,7 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.when;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -143,10 +145,11 @@
     protected void setWifiState(boolean connected, String ssid) {
         Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         NetworkInfo networkInfo = Mockito.mock(NetworkInfo.class);
-        Mockito.when(networkInfo.isConnected()).thenReturn(connected);
+        when(networkInfo.isConnected()).thenReturn(connected);
 
-        //TODO(b/69974497) mock of mWifiManager.getConnectionInfo() needed
-        // Mockito.when(wifiInfo.getSSID()).thenReturn(ssid);
+        WifiInfo wifiInfo = Mockito.mock(WifiInfo.class);
+        when(wifiInfo.getSSID()).thenReturn(ssid);
+        when(mMockWm.getConnectionInfo()).thenReturn(wifiInfo);
 
         i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         mNetworkController.onReceive(mContext, i);
@@ -175,8 +178,7 @@
         assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
         assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
         assertEquals("WiFi signal, in quick settings", icon, iconState.icon);
-        // TODO(b/69974497) Need to mock mWifiManager.getConnectionInfo() to supply the ssid.
-        // assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue());
+        assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue());
     }
 
     protected void verifyLastWifiIcon(boolean visible, int icon) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 7ca9d73..f76de5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -19,11 +19,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -32,6 +36,9 @@
 import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.UserManager;
 import android.security.IKeyChainService;
 import android.support.test.runner.AndroidJUnit4;
@@ -61,6 +68,7 @@
     private final UserManager mUserManager = mock(UserManager.class);
     private SecurityControllerImpl mSecurityController;
     private CountDownLatch mStateChangedLatch;
+    private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
 
     // implementing SecurityControllerCallback
     @Override
@@ -72,7 +80,7 @@
     public void setUp() throws Exception {
         mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
         mContext.addMockSystemService(Context.USER_SERVICE, mUserManager);
-        mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
+        mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mConnectivityManager);
 
         Intent intent = new Intent(IKeyChainService.class.getName());
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
@@ -176,4 +184,12 @@
         //assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS));
         //assertFalse(mSecurityController.hasCACertInCurrentUser());
     }
+
+    @Test
+    public void testNetworkRequest() {
+        verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
+                (NetworkRequest request) -> request.networkCapabilities.getUids() == null
+                        && request.networkCapabilities.getCapabilities().length == 0
+                ), any(NetworkCallback.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
new file mode 100644
index 0000000..28d1aff
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.function.Consumer;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationRoundnessManagerTest extends SysuiTestCase {
+
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private HashSet<View> mAnimatedChildren = new HashSet<>();
+    private Runnable mRoundnessCallback = mock(Runnable.class);
+    private ExpandableNotificationRow mFirst;
+    private ExpandableNotificationRow mSecond;
+
+    @Before
+    public void setUp() throws Exception {
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+        mFirst = testHelper.createRow();
+        mFirst.setHeadsUpAnimatingAwayListener(animatingAway
+                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
+        mSecond = testHelper.createRow();
+        mSecond.setHeadsUpAnimatingAwayListener(animatingAway
+                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
+        mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
+        mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
+        mRoundnessManager.setExpanded(1.0f, 1.0f);
+    }
+
+    @Test
+    public void testCallbackCalledWhenSecondChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testCallbackCalledWhenFirstChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testRoundnessSetOnLast() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundnessSetOnNew() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testCompleteReplacement() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testNotCalledWhenRemoved() {
+        mFirst.setRemoved();
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenPinnedAndCollapsed() {
+        mFirst.setPinned(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenGoingAwayAndCollapsed() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedNormalRoundingWhenExpanded() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpRoundedIfPushingUp() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundingUpdatedWhenAnimatingAwayTrue() {
+        mRoundnessManager.setExpanded(0.0f, 0.0f);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        mFirst.setHeadsUpAnimatingAway(true);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+
+    @Test
+    public void testRoundingUpdatedWhenAnimatingAwayFalse() {
+        mRoundnessManager.setExpanded(0.0f, 0.0f);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        mFirst.setHeadsUpAnimatingAway(true);
+        mFirst.setHeadsUpAnimatingAway(false);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 62d941a..9417f04 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3964,7 +3964,8 @@
     // Type TYPE_FAILURE: The request failed
     // Package: Package of app that is autofilled
     // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
-    // Tag FIELD_AUTOFILL_NUM_DATASETS: The number of datasets returned (only in success case)
+    // Tag FIELD_AUTOFILL_NUM_DATASETS: The number of datasets returned in the response, or -1 if
+    // the service returned a null response.
     // NOTE: starting on OS P, it also added:
     // Type TYPE_CLOSE: Service returned a null response.
     // Tag FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS: if service requested field classification,
@@ -5511,6 +5512,41 @@
     // OS: P
     SETTINGS_PREVENT_RINGING = 1360;
 
+    // ACTION: Settings > Battery settings > Battery tip > Open app restriction page
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_OPEN_APP_RESTRICTION_PAGE = 1361;
+
+    // ACTION: Settings > Battery settings > Battery tip > Restrict app
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_RESTRICT_APP = 1362;
+
+    // ACTION: Settings > Battery settings > Battery tip > Unrestrict app
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_UNRESTRICT_APP = 1363;
+
+    // ACTION: Settings > Battery settings > Battery tip > Open smart battery page
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_OPEN_SMART_BATTERY = 1364;
+
+    // ACTION: Settings > Battery settings > Battery tip > Turn on battery saver
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_TIP_TURN_ON_BATTERY_SAVER = 1365;
+
+    // FIELD: type of anomaly in settings app
+    // CATEGORY: SETTINGS
+    // OS: P
+    FIELD_ANOMALY_TYPE = 1366;
+
+    // ACTION: Settings > Anomaly receiver > Anomaly received
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ANOMALY_TRIGGERED = 1367;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 0763fa1..39d0070 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1134,14 +1134,38 @@
     NUM_CLIENTS_CHANGED = 2;
   }
 
+  // Soft AP channel bandwidth types
+  enum ChannelBandwidth {
+
+    BANDWIDTH_INVALID = 0;
+
+    BANDWIDTH_20_NOHT = 1;
+
+    BANDWIDTH_20 = 2;
+
+    BANDWIDTH_40 = 3;
+
+    BANDWIDTH_80 = 4;
+
+    BANDWIDTH_80P80 = 5;
+
+    BANDWIDTH_160 = 6;
+  }
+
   // Type of event being recorded
   optional SoftApEventType event_type = 1;
 
-  // Absolute time when event happened
+  // Time passed since last boot in milliseconds
   optional int64 time_stamp_millis = 2;
 
   // Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
   optional int32 num_connected_clients = 3;
+
+  // Channel frequency used for Soft AP
+  optional int32 channel_frequency = 4;
+
+  // Channel bandwidth used for Soft AP
+  optional ChannelBandwidth channel_bandwidth = 5;
 }
 
 // Wps connection metrics
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ed068b9..5c5978a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -88,7 +88,7 @@
 
     final int mId;
 
-    final AccessibilityServiceInfo mAccessibilityServiceInfo;
+    protected final AccessibilityServiceInfo mAccessibilityServiceInfo;
 
     // Lock must match the one used by AccessibilityManagerService
     protected final Object mLock;
@@ -340,6 +340,10 @@
         }
     }
 
+    public int getCapabilities() {
+        return mAccessibilityServiceInfo.getCapabilities();
+    }
+
     int getRelevantEventTypes() {
         return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0)
                 | mEventTypes;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ecd47e8..8941b49 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -621,7 +621,7 @@
             for (int i = 0; i < serviceCount; ++i) {
                 final AccessibilityServiceConnection service = services.get(i);
                 if ((service.mFeedbackType & feedbackType) != 0) {
-                    result.add(service.mAccessibilityServiceInfo);
+                    result.add(service.getServiceInfo());
                 }
             }
             return result;
@@ -1874,7 +1874,7 @@
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
-            if ((service.mAccessibilityServiceInfo.getCapabilities()
+            if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
                 userState.mIsPerformGesturesEnabled = true;
                 return;
@@ -1888,7 +1888,7 @@
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if (service.mRequestFilterKeyEvents
-                    && (service.mAccessibilityServiceInfo.getCapabilities()
+                    && (service.getCapabilities()
                             & AccessibilityServiceInfo
                             .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
                 userState.mIsFilterKeyEventsEnabled = true;
@@ -2124,7 +2124,7 @@
             // Starting in JB-MR2 we request an accessibility service to declare
             // certain capabilities in its meta-data to allow it to enable the
             // corresponding features.
-            if ((service.mAccessibilityServiceInfo.getCapabilities()
+            if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) {
                 return true;
             }
@@ -3446,22 +3446,22 @@
         }
 
         public boolean canRetrieveWindowContentLocked(AbstractAccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
         }
 
         public boolean canControlMagnification(AbstractAccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
         }
 
         public boolean canPerformGestures(AccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
         }
 
         public boolean canCaptureFingerprintGestures(AccessibilityServiceConnection service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
+            return (service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES) != 0;
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 89bf82d..eb18f06 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -165,7 +165,14 @@
         }
     }
 
-    public void initializeService() {
+    @Override
+    public AccessibilityServiceInfo getServiceInfo() {
+        // Update crashed data
+        mAccessibilityServiceInfo.crashed = mWasConnectedAndDied;
+        return mAccessibilityServiceInfo;
+    }
+
+    private void initializeService() {
         IAccessibilityServiceClient serviceInterface = null;
         synchronized (mLock) {
             UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 5b5d18f..9f44197 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -55,7 +55,7 @@
  * magnification region. If a value is out of bounds, it will be adjusted to guarantee these
  * constraints.
  */
-class MagnificationController implements Handler.Callback {
+public class MagnificationController implements Handler.Callback {
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "MagnificationController";
 
diff --git a/services/art-profile b/services/art-profile
index d2cde02..93e580a 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -10677,7 +10677,6 @@
 PLcom/android/server/location/CountryDetectorBase;-><init>(Landroid/content/Context;)V
 PLcom/android/server/location/CountryDetectorBase;->notifyListener(Landroid/location/Country;)V
 PLcom/android/server/location/CountryDetectorBase;->setCountryListener(Landroid/location/CountryListener;)V
-PLcom/android/server/location/FlpHardwareProvider;->isSupported()Z
 PLcom/android/server/location/GeocoderProxy;-><init>(Landroid/content/Context;IIILandroid/os/Handler;)V
 PLcom/android/server/location/GeocoderProxy;->bind()Z
 PLcom/android/server/location/GeocoderProxy;->createAndBind(Landroid/content/Context;IIILandroid/os/Handler;)Lcom/android/server/location/GeocoderProxy;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a5339e0..009e723 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -80,6 +80,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.autofill.AutofillManagerService.PackageCompatState;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.FileDescriptor;
@@ -108,10 +109,6 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
-    // TODO(b/74445943): temporary work around until P Development Preview 3 is branched
-    private static final List<String> DEFAULT_BUTTONS = Arrays.asList("url_bar",
-            "location_bar_edit_text");
-
     private final Context mContext;
     private final AutoFillUI mUi;
 
@@ -575,7 +572,7 @@
     private String getWhitelistedCompatModePackagesFromSettings() {
         return Settings.Global.getString(
                 mContext.getContentResolver(),
-                Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
     }
 
     @Nullable
@@ -600,7 +597,7 @@
             final List<String> urlBarIds;
             if (urlBlockIndex == -1) {
                 packageName = packageBlock;
-                urlBarIds = DEFAULT_BUTTONS; // TODO(b/74445943): back to null
+                urlBarIds = null;
             } else {
                 if (packageBlock.charAt(packageBlock.length() - 1)
                         != COMPAT_PACKAGE_URL_IDS_BLOCK_END) {
@@ -655,7 +652,7 @@
     /**
      * Compatibility mode metadata per package.
      */
-    private static final class PackageCompatState {
+    static final class PackageCompatState {
         private final long maxVersionCode;
         private final String[] urlBarResourceIds;
 
@@ -666,8 +663,8 @@
 
         @Override
         public String toString() {
-            return "PackageCompatState: [maxVersionCode=" + maxVersionCode
-                    + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds) + "]";
+            return "maxVersionCode=" + maxVersionCode
+                    + ", urlBarResourceIds=" + Arrays.toString(urlBarResourceIds);
         }
     }
 
@@ -756,6 +753,25 @@
                 }
             }
         }
+
+        private void dump(String prefix, PrintWriter pw) {
+             if (mUserSpecs == null) {
+                 pw.println("N/A");
+                 return;
+             }
+             pw.println();
+             final String prefix2 = prefix + "  ";
+             for (int i = 0; i < mUserSpecs.size(); i++) {
+                 final int user = mUserSpecs.keyAt(i);
+                 pw.print(prefix); pw.print("User: "); pw.println(user);
+                 final ArrayMap<String,PackageCompatState> perUser = mUserSpecs.get(i);
+                 for (int j = 0; j < perUser.size(); j++) {
+                     final String packageName = perUser.keyAt(j);
+                     final PackageCompatState state = perUser.valueAt(j);
+                     pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+                }
+            }
+        }
     }
 
     final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
@@ -1121,6 +1137,7 @@
 
             boolean oldDebug = sDebug;
             final String prefix = "  ";
+            final String prefix2 = "    ";
             try {
                 synchronized (mLock) {
                     oldDebug = sDebug;
@@ -1145,8 +1162,8 @@
                     }
                     mUi.dump(pw);
                     pw.print("Autofill Compat State: ");
-                    pw.println(mAutofillCompatState.mUserSpecs);
-                    pw.print(prefix); pw.print("from settings: ");
+                    mAutofillCompatState.dump(prefix2, pw);
+                    pw.print(prefix2); pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
                 }
                 if (showHistory) {
@@ -1179,7 +1196,7 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES), false, this,
+                    Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
                     UserHandle.USER_ALL);
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 54ea3ba..ef0d7e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1189,7 +1189,7 @@
     boolean isFieldClassificationEnabledLocked() {
         return Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
-                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0,
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
                 mUserId) == 1;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 5c41f3f..7bb532e 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -26,6 +26,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.view.WindowManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
@@ -107,6 +108,13 @@
     }
 
     @NonNull
+    public static String paramsToString(@NonNull WindowManager.LayoutParams params) {
+        final StringBuilder builder = new StringBuilder(25);
+        params.dumpDimensions(builder);
+        return builder.toString();
+    }
+
+    @NonNull
     static ArrayMap<AutofillId, AutofillValue> getFields(@NonNull Dataset dataset) {
         final ArrayList<AutofillId> ids = dataset.getFieldIds();
         final ArrayList<AutofillValue> values = dataset.getFieldValues();
@@ -128,7 +136,7 @@
         return log;
     }
 
-    public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) {
+    public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable CharSequence text) {
         if (text == null) {
             pw.println("null");
         } else {
@@ -173,6 +181,7 @@
      * @param structure Assist structure
      * @param urlBarIds list of ids; only the first id found will be sanitized.
      */
+    @Nullable
     public static void sanitizeUrlBar(@NonNull AssistStructure structure,
             @NonNull String[] urlBarIds) {
         final ViewNode urlBarNode = findViewNode(structure, (node) -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 26598a2..1e1de35 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -596,6 +596,9 @@
             }
             if (response == null) {
                 processNullResponseLocked(requestFlags);
+                mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
+                        .setType(MetricsEvent.TYPE_SUCCESS)
+                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1));
                 return;
             }
 
@@ -1438,11 +1441,25 @@
                 final AutofillValue filledValue = viewState.getAutofilledValue();
 
                 if (!value.equals(filledValue)) {
-                    if (sDebug) {
-                        Slog.d(TAG, "found a change on required " + id + ": " + filledValue
-                                + " => " + value);
+                    boolean changed = true;
+                    if (filledValue == null) {
+                        // Dataset was not autofilled, make sure initial value didn't change.
+                        final AutofillValue initialValue = getValueFromContextsLocked(id);
+                        if (initialValue != null && initialValue.equals(value)) {
+                            if (sDebug) {
+                                Slog.d(TAG, "id " + id + " is part of dataset but initial value "
+                                        + "didn't change: " + value);
+                            }
+                            changed = false;
+                        }
                     }
-                    atLeastOneChanged = true;
+                    if (changed) {
+                        if (sDebug) {
+                            Slog.d(TAG, "found a change on required " + id + ": " + filledValue
+                                    + " => " + value);
+                        }
+                        atLeastOneChanged = true;
+                    }
                 }
             }
         }
@@ -1874,7 +1891,7 @@
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
 
-                // TODO(b/70407264): for optimization purposes, should also ignore if change is
+                // TODO(b/73648631): for optimization purposes, should also ignore if change is
                 // detectable, and batch-send them when the session is finished (but that will
                 // require tracking detectable fields on AutofillManager)
                 if (isIgnored) {
@@ -2060,8 +2077,11 @@
             if (saveTriggerId != null) {
                 writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
             }
-            saveOnAllViewsInvisible =
-                    (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+            int flags = saveInfo.getFlags();
+            if (mCompatMode) {
+                flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
+            }
+            saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
 
             // We only need to track views if we want to save once they become invisible.
             if (saveOnAllViewsInvisible) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 0dbdc13..03c5850 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -206,29 +206,51 @@
 
     @Override
     public String toString() {
-        return "ViewState: [id=" + id + ", datasetId=" + mDatasetId
-                + ", currentValue=" + mCurrentValue
-                + ", autofilledValue=" + mAutofilledValue
-                + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
+        final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
+        if (mDatasetId != null) {
+            builder.append("datasetId:" ).append(mDatasetId);
+        }
+        builder.append("state:" ).append(getStateAsString());
+        if (mCurrentValue != null) {
+            builder.append("currentValue:" ).append(mCurrentValue);
+        }
+        if (mAutofilledValue != null) {
+            builder.append("autofilledValue:" ).append(mAutofilledValue);
+        }
+        if (mSanitizedValue != null) {
+            builder.append("sanitizedValue:" ).append(mSanitizedValue);
+        }
+        if (mVirtualBounds != null) {
+            builder.append("virtualBounds:" ).append(mVirtualBounds);
+        }
+        return builder.toString();
     }
 
     void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("id:" ); pw.println(this.id);
-        pw.print(prefix); pw.print("datasetId:" ); pw.println(this.mDatasetId);
+        pw.print(prefix); pw.print("id:" ); pw.println(id);
+        if (mDatasetId != null) {
+            pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
+        }
         pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
-        pw.print(prefix); pw.print("response:");
-        if (mResponse == null) {
-            pw.println("N/A");
-        } else {
+        if (mResponse != null) {
+            pw.print(prefix); pw.print("response:");
             if (sVerbose) {
                 pw.println(mResponse);
             } else {
-                pw.println(mResponse.getRequestId());
+                pw.print("id=");pw.println(mResponse.getRequestId());
             }
         }
-        pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
-        pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
-        pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
-        pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+        if (mCurrentValue != null) {
+            pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
+        }
+        if (mAutofilledValue != null) {
+            pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+        }
+        if (mSanitizedValue != null) {
+            pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
+        }
+        if (mVirtualBounds != null) {
+            pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
+        }
     }
 }
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index ef4656b..edfc412 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.autofill.ui;
 
+import static com.android.server.autofill.Helper.paramsToString;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 
@@ -37,7 +38,6 @@
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
@@ -568,12 +568,24 @@
 
         @Override
         public String toString() {
-            return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId())
-                    + ", value=" + (value == null ? "null" : value.length() + "_chars")
-                    + ", filterable=" + filterable
-                    + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars")
-                    + ", view=" + view.getAutofillId()
-                    + "]";
+            final StringBuilder builder = new StringBuilder("ViewItem:[view=")
+                    .append(view.getAutofillId());
+            final String datasetId = dataset == null ? null : dataset.getId();
+            if (datasetId != null) {
+                builder.append(", dataset=").append(datasetId);
+            }
+            if (value != null) {
+                // Cannot print value because it could contain PII
+                builder.append(", value=").append(value.length()).append("_chars");
+            }
+            if (filterable) {
+                builder.append(", filterable");
+            }
+            if (filter != null) {
+                // Filter should not have PII, but it could be a huge regexp
+                builder.append(", filter=").append(filter.pattern().length()).append("_chars");
+            }
+            return builder.append(']').toString();
         }
     }
 
@@ -583,8 +595,7 @@
                 boolean fitsSystemWindows, int layoutDirection) {
             if (sVerbose) {
                 Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
-                        + ", epicenter="+ transitionEpicenter + ", dir=" + layoutDirection
-                        + ", params=" + p);
+                        + ", params=" + paramsToString(p));
             }
             UiThread.getHandler().post(() -> mWindow.show(p));
         }
@@ -616,7 +627,9 @@
          * Shows the window.
          */
         public void show(WindowManager.LayoutParams params) {
-            if (sVerbose) Slog.v(TAG, "show(): showing=" + mShowing + ", params="+  params);
+            if (sVerbose) {
+                Slog.v(TAG, "show(): showing=" + mShowing + ", params=" + paramsToString(params));
+            }
             try {
                 // Okay here is a bit of voodoo - we want to show the window as system
                 // controlled one so it covers app windows - adjust the params accordingly.
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index 16be680..cec4f1a 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -70,17 +70,12 @@
  * - Temporary power save whitelist
  * - Global "force all apps standby" mode enforced by battery saver.
  *
- * TODO: Make it a LocalService.
- *
  * Test:
   atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
  */
 public class AppStateTracker {
-    private static final String TAG = "ForceAppStandbyTracker";
-    private static final boolean DEBUG = true;
-
-    @GuardedBy("AppStateTracker.class")
-    private static AppStateTracker sInstance;
+    private static final String TAG = "AppStateTracker";
+    private static final boolean DEBUG = false;
 
     private final Object mLock = new Object();
     private final Context mContext;
@@ -266,6 +261,12 @@
                 // we need to deliver the allow-while-idle alarms for this uid, package
                 unblockAllUnrestrictedAlarms();
             }
+
+            if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) {
+                Slog.v(TAG, "Package " + packageName + "/" + uid
+                        + " toggled into fg service restriction");
+                stopForegroundServicesForUidPackage(uid, packageName);
+            }
         }
 
         /**
@@ -359,6 +360,13 @@
         }
 
         /**
+         * Called when an app goes into forced app standby and its foreground
+         * services need to be removed from that state.
+         */
+        public void stopForegroundServicesForUidPackage(int uid, String packageName) {
+        }
+
+        /**
          * Called when the job restrictions for multiple UIDs might have changed, so the alarm
          * manager should re-evaluate all restrictions for all blocked jobs.
          */
@@ -1062,6 +1070,16 @@
     }
 
     /**
+     * @return whether foreground services should be suppressed in the background
+     * due to forced app standby for the given app
+     */
+    public boolean areForegroundServicesRestricted(int uid, @NonNull String packageName) {
+        synchronized (mLock) {
+            return isRunAnyRestrictedLocked(uid, packageName);
+        }
+    }
+
+    /**
      * @return whether force-app-standby is effective for a UID package-name.
      */
     private boolean isRestricted(int uid, @NonNull String packageName,
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
new file mode 100644
index 0000000..2ed4516
--- /dev/null
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -0,0 +1,55 @@
+/*
+ * 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 android.os.Binder;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.os.BinderCallsStats;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class BinderCallsStatsService extends Binder {
+
+    private static final String TAG = "BinderCallsStatsService";
+
+    private static final String PERSIST_SYS_BINDER_CPU_STATS_TRACKING
+            = "persist.sys.binder_cpu_stats_tracking";
+
+    public static void start() {
+        BinderCallsStatsService service = new BinderCallsStatsService();
+        ServiceManager.addService("binder_calls_stats", service);
+        // TODO Enable by default on eng/userdebug builds
+        boolean trackingEnabledDefault = false;
+        boolean trackingEnabled = SystemProperties.getBoolean(PERSIST_SYS_BINDER_CPU_STATS_TRACKING,
+                trackingEnabledDefault);
+
+        if (trackingEnabled) {
+            Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
+                    + PERSIST_SYS_BINDER_CPU_STATS_TRACKING);
+            BinderCallsStats.getInstance().setTrackingEnabled(true);
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        BinderCallsStats.getInstance().dump(pw);
+    }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 30d31b8..088f366 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1386,6 +1386,12 @@
         }
     }
 
+    private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
+        if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+            nc.addCapability(NET_CAPABILITY_FOREGROUND);
+        }
+    }
+
     @Override
     public NetworkState[] getAllNetworkState() {
         // Require internal since we're handing out IMSI details
@@ -4411,15 +4417,13 @@
 
         NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         restrictRequestUidsForCaller(nc);
-        if (!ConnectivityManager.checkChangePermission(mContext)) {
-            // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
-            // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
-            // onLost and onAvailable callbacks when networks move in and out of the background.
-            // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
-            // can't request networks.
-            nc.addCapability(NET_CAPABILITY_FOREGROUND);
-        }
-        ensureValidNetworkSpecifier(networkCapabilities);
+        // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
+        // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
+        // onLost and onAvailable callbacks when networks move in and out of the background.
+        // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
+        // can't request networks.
+        restrictBackgroundRequestForCaller(nc);
+        ensureValidNetworkSpecifier(nc);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 5b57c14..f6ff359 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3663,16 +3663,21 @@
             }
         }
 
+        boolean reenableMinimumNonAuxSystemImes = false;
         // TODO: The following code should find better place to live.
         if (!resetDefaultEnabledIme) {
             boolean enabledImeFound = false;
+            boolean enabledNonAuxImeFound = false;
             final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodListLocked();
             final int N = enabledImes.size();
             for (int i = 0; i < N; ++i) {
                 final InputMethodInfo imi = enabledImes.get(i);
                 if (mMethodList.contains(imi)) {
                     enabledImeFound = true;
-                    break;
+                    if (!imi.isAuxiliaryIme()) {
+                        enabledNonAuxImeFound = true;
+                        break;
+                    }
                 }
             }
             if (!enabledImeFound) {
@@ -3681,12 +3686,18 @@
                 }
                 resetDefaultEnabledIme = true;
                 resetSelectedInputMethodAndSubtypeLocked("");
+            } else if (!enabledNonAuxImeFound) {
+                if (DEBUG) {
+                    Slog.i(TAG, "All the enabled non-Aux IMEs are gone. Do partial reset.");
+                }
+                reenableMinimumNonAuxSystemImes = true;
             }
         }
 
-        if (resetDefaultEnabledIme) {
+        if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
             final ArrayList<InputMethodInfo> defaultEnabledIme =
-                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
+                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList,
+                            reenableMinimumNonAuxSystemImes);
             final int N = defaultEnabledIme.size();
             for (int i = 0; i < N; ++i) {
                 final InputMethodInfo imi =  defaultEnabledIme.get(i);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 45a4dfb9..d09a161 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -36,6 +36,7 @@
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
 import android.net.IpSecUdpEncapResponse;
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
@@ -618,10 +619,8 @@
                                 spi,
                                 mConfig.getMarkValue(),
                                 mConfig.getMarkMask());
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -677,14 +676,14 @@
         @Override
         public void freeUnderlyingResources() {
             try {
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecDeleteSecurityAssociation(
-                                mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
+                if (!mOwnedByTransform) {
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecDeleteSecurityAssociation(
+                                    mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+                }
+            } catch (ServiceSpecificException | RemoteException e) {
+                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
             }
 
             mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -829,15 +828,13 @@
                                         0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
                     }
                 }
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
+            } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
                         TAG,
                         "Failed to delete VTI with interface name: "
                                 + mInterfaceName
                                 + " and id: "
-                                + mResourceId);
+                                + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -1319,7 +1316,9 @@
      * from multiple local IP addresses over the same tunnel.
      */
     @Override
-    public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
+    public synchronized void addAddressToTunnelInterface(
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1327,8 +1326,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Add address to TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceAddAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1337,7 +1349,8 @@
      */
     @Override
     public synchronized void removeAddressFromTunnelInterface(
-            int tunnelResourceId, String localAddr) {
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1345,8 +1358,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Remove address from TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceDelAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1467,6 +1493,13 @@
         IpSecAlgorithm crypt = c.getEncryption();
         IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
 
+        String cryptName;
+        if (crypt == null) {
+            cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
+        } else {
+            cryptName = crypt.getName();
+        }
+
         mSrvConfig
                 .getNetdInstance()
                 .ipSecAddSecurityAssociation(
@@ -1481,7 +1514,7 @@
                         (auth != null) ? auth.getName() : "",
                         (auth != null) ? auth.getKey() : new byte[] {},
                         (auth != null) ? auth.getTruncationLengthBits() : 0,
-                        (crypt != null) ? crypt.getName() : "",
+                        cryptName,
                         (crypt != null) ? crypt.getKey() : new byte[] {},
                         (crypt != null) ? crypt.getTruncationLengthBits() : 0,
                         (authCrypt != null) ? authCrypt.getName() : "",
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 65e90bad..26b83f5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
@@ -82,8 +83,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.server.location.ActivityRecognitionProxy;
-import com.android.server.location.FlpHardwareProvider;
-import com.android.server.location.FusedProxy;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
 import com.android.server.location.GeofenceProxy;
@@ -491,13 +490,6 @@
         if (gpsProvider != null && gpsProvider.isEnabled()) {
             gpsProvider.disable();
         }
-
-        // it is needed to check if FLP HW provider is supported before accessing the instance, this
-        // avoids an exception to be thrown by the singleton factory method
-        if (FlpHardwareProvider.isSupported()) {
-            FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
-            flpHardwareProvider.cleanup();
-        }
     }
 
     /**
@@ -686,27 +678,6 @@
             Slog.e(TAG, "no geocoder provider found");
         }
 
-        // bind to fused hardware provider if supported
-        // in devices without support, requesting an instance of FlpHardwareProvider will raise an
-        // exception, so make sure we only do that when supported
-        FlpHardwareProvider flpHardwareProvider;
-        if (FlpHardwareProvider.isSupported()) {
-            flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
-            FusedProxy fusedProxy = FusedProxy.createAndBind(
-                    mContext,
-                    mLocationHandler,
-                    flpHardwareProvider.getLocationHardware(),
-                    com.android.internal.R.bool.config_enableHardwareFlpOverlay,
-                    com.android.internal.R.string.config_hardwareFlpPackageName,
-                    com.android.internal.R.array.config_locationProviderPackageNames);
-            if (fusedProxy == null) {
-                Slog.d(TAG, "Unable to bind FusedProxy.");
-            }
-        } else {
-            flpHardwareProvider = null;
-            Slog.d(TAG, "FLP HAL not supported");
-        }
-
         // bind to geofence provider
         GeofenceProxy provider = GeofenceProxy.createAndBind(
                 mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
@@ -714,7 +685,7 @@
                 com.android.internal.R.array.config_locationProviderPackageNames,
                 mLocationHandler,
                 mGpsGeofenceProxy,
-                flpHardwareProvider != null ? flpHardwareProvider.getGeofenceHardware() : null);
+                null);
         if (provider == null) {
             Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
         }
@@ -1161,11 +1132,12 @@
      * Returns the model name of the GNSS hardware.
      */
     @Override
+    @Nullable
     public String getGnssHardwareModelName() {
         if (mGnssSystemInfoProvider != null) {
             return mGnssSystemInfoProvider.getGnssHardwareModelName();
         } else {
-            return LocationManager.GNSS_HARDWARE_MODEL_NAME_UNKNOWN;
+            return null;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 4a65733..ff65951 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -24,8 +24,6 @@
 import android.util.PrintWriterPrinter;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.ActiveInstrumentationProto;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index eb4e32e..e84c5f4 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -56,9 +56,10 @@
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
+import com.android.server.AppStateTracker;
+import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.ActivityManagerService.NeededUriGrants;
-import com.android.server.am.proto.ActiveServicesProto;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -165,6 +166,44 @@
     };
 
     /**
+     * Watch for apps being put into forced app standby, so we can step their fg
+     * services down.
+     */
+    class ForcedStandbyListener extends AppStateTracker.Listener {
+        @Override
+        public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
+            synchronized (mAm) {
+                final ServiceMap smap = getServiceMapLocked(UserHandle.getUserId(uid));
+                final int N = smap.mServicesByName.size();
+                final ArrayList<ServiceRecord> toStop = new ArrayList<>(N);
+                for (int i = 0; i < N; i++) {
+                    final ServiceRecord r = smap.mServicesByName.valueAt(i);
+                    if (uid == r.serviceInfo.applicationInfo.uid
+                            || packageName.equals(r.serviceInfo.packageName)) {
+                        if (r.isForeground) {
+                            toStop.add(r);
+                        }
+                    }
+                }
+
+                // Now stop them all
+                final int numToStop = toStop.size();
+                if (numToStop > 0 && DEBUG_FOREGROUND_SERVICE) {
+                    Slog.i(TAG, "Package " + packageName + "/" + uid
+                            + " entering FAS with foreground services");
+                }
+                for (int i = 0; i < numToStop; i++) {
+                    final ServiceRecord r = toStop.get(i);
+                    if (DEBUG_FOREGROUND_SERVICE) {
+                        Slog.i(TAG, "  Stopping fg for service " + r);
+                    }
+                    setServiceForegroundInnerLocked(r, 0, null, 0);
+                }
+            }
+        }
+    }
+
+    /**
      * Information about an app that is currently running one or more foreground services.
      * (This maps directly to the running apps we show in the notification.)
      */
@@ -302,6 +341,11 @@
                 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
     }
 
+    void systemServicesReady() {
+        AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
+        ast.addListener(new ForcedStandbyListener());
+    }
+
     ServiceRecord getServiceByNameLocked(ComponentName name, int callingUser) {
         // TODO: Deal with global services
         if (DEBUG_MU)
@@ -327,6 +371,12 @@
         return getServiceMapLocked(callingUser).mServicesByName;
     }
 
+    private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
+        final int mode = mAm.mAppOpsService.checkOperation(
+                AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);
+        return (mode != AppOpsManager.MODE_ALLOWED);
+    }
+
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
             throws TransactionTooLargeException {
@@ -365,13 +415,24 @@
             return null;
         }
 
+        // If the app has strict background restrictions, we treat any service
+        // start analogously to the legacy-app forced-restrictions case.
+        boolean forcedStandby = false;
+        if (appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
+            if (DEBUG_FOREGROUND_SERVICE) {
+                Slog.d(TAG, "Forcing bg-only service start only for "
+                        + r.name.flattenToShortString());
+            }
+            forcedStandby = true;
+        }
+
         // If this isn't a direct-to-foreground start, check our ability to kick off an
         // arbitrary service
-        if (!r.startRequested && !fgRequired) {
+        if (forcedStandby || (!r.startRequested && !fgRequired)) {
             // Before going further -- if this app is not allowed to start services in the
             // background, then at this point we aren't going to let it period.
             final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
-                    r.appInfo.targetSdkVersion, callingPid, false, false);
+                    r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
             if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                 Slog.w(TAG, "Background start not allowed: service "
                         + service + " to " + r.name.flattenToShortString()
@@ -625,7 +686,7 @@
                 ServiceRecord service = services.mServicesByName.valueAt(i);
                 if (service.appInfo.uid == uid && service.startRequested) {
                     if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
-                            service.appInfo.targetSdkVersion, -1, false, false)
+                            service.appInfo.targetSdkVersion, -1, false, false, false)
                             != ActivityManager.APP_START_MODE_NORMAL) {
                         if (stopping == null) {
                             stopping = new ArrayList<>();
@@ -1019,7 +1080,10 @@
         }
     }
 
-    private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
+    /**
+     * @param id Notification ID.  Zero === exit foreground state for the given service.
+     */
+    private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
             Notification notification, int flags) {
         if (id != 0) {
             if (notification == null) {
@@ -1061,44 +1125,56 @@
                 mAm.mHandler.removeMessages(
                         ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
             }
-            if (r.foregroundId != id) {
-                cancelForegroundNotificationLocked(r);
-                r.foregroundId = id;
-            }
-            notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-            r.foregroundNoti = notification;
-            if (!r.isForeground) {
-                final ServiceMap smap = getServiceMapLocked(r.userId);
-                if (smap != null) {
-                    ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
-                    if (active == null) {
-                        active = new ActiveForegroundApp();
-                        active.mPackageName = r.packageName;
-                        active.mUid = r.appInfo.uid;
-                        active.mShownWhileScreenOn = mScreenOn;
-                        if (r.app != null) {
-                            active.mAppOnTop = active.mShownWhileTop =
-                                    r.app.uidRecord.curProcState
-                                            <= ActivityManager.PROCESS_STATE_TOP;
-                        }
-                        active.mStartTime = active.mStartVisibleTime
-                                = SystemClock.elapsedRealtime();
-                        smap.mActiveForegroundApps.put(r.packageName, active);
-                        requestUpdateActiveForegroundAppsLocked(smap, 0);
-                    }
-                    active.mNumActive++;
+
+            // Apps under strict background restrictions simply don't get to have foreground
+            // services, so now that we've enforced the startForegroundService() contract
+            // we only do the machinery of making the service foreground when the app
+            // is not restricted.
+            if (!appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
+                if (r.foregroundId != id) {
+                    cancelForegroundNotificationLocked(r);
+                    r.foregroundId = id;
                 }
-                r.isForeground = true;
-                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
-                        StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+                notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+                r.foregroundNoti = notification;
+                if (!r.isForeground) {
+                    final ServiceMap smap = getServiceMapLocked(r.userId);
+                    if (smap != null) {
+                        ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
+                        if (active == null) {
+                            active = new ActiveForegroundApp();
+                            active.mPackageName = r.packageName;
+                            active.mUid = r.appInfo.uid;
+                            active.mShownWhileScreenOn = mScreenOn;
+                            if (r.app != null) {
+                                active.mAppOnTop = active.mShownWhileTop =
+                                        r.app.uidRecord.curProcState
+                                        <= ActivityManager.PROCESS_STATE_TOP;
+                            }
+                            active.mStartTime = active.mStartVisibleTime
+                                    = SystemClock.elapsedRealtime();
+                            smap.mActiveForegroundApps.put(r.packageName, active);
+                            requestUpdateActiveForegroundAppsLocked(smap, 0);
+                        }
+                        active.mNumActive++;
+                    }
+                    r.isForeground = true;
+                    StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+                            r.appInfo.uid, r.shortName,
+                            StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+                }
+                r.postNotification();
+                if (r.app != null) {
+                    updateServiceForegroundLocked(r.app, true);
+                }
+                getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
+                mAm.notifyPackageUse(r.serviceInfo.packageName,
+                        PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
+            } else {
+                if (DEBUG_FOREGROUND_SERVICE) {
+                    Slog.d(TAG, "Suppressing startForeground() for FAS " + r);
+                }
             }
-            r.postNotification();
-            if (r.app != null) {
-                updateServiceForegroundLocked(r.app, true);
-            }
-            getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
-            mAm.notifyPackageUse(r.serviceInfo.packageName,
-                                 PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
         } else {
             if (r.isForeground) {
                 final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -1106,7 +1182,8 @@
                     decActiveForegroundAppLocked(smap, r);
                 }
                 r.isForeground = false;
-                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
+                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
+                        r.appInfo.uid, r.shortName,
                         StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
                 if (r.app != null) {
                     mAm.updateLruProcessLocked(r.app, false, null);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 4498077..4a8bc87 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -35,9 +35,9 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
-import static com.android.server.am.proto.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.STACKS;
+import static com.android.server.am.ActivityDisplayProto.ID;
 
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 063c1f1..955035b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -123,7 +123,6 @@
 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;
@@ -187,6 +186,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
@@ -204,7 +204,6 @@
 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;
 
@@ -389,8 +388,8 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.View;
 import android.view.WindowManager;
-
 import android.view.autofill.AutofillManagerInternal;
+
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -442,19 +441,19 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.proto.ActivityManagerServiceProto;
-import com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto;
-import com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto;
-import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto;
-import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.proto.ActivityManagerServiceDumpServicesProto;
-import com.android.server.am.proto.GrantUriProto;
-import com.android.server.am.proto.ImportanceTokenProto;
-import com.android.server.am.proto.MemInfoDumpProto;
-import com.android.server.am.proto.NeededUriGrantsProto;
-import com.android.server.am.proto.ProcessOomProto;
-import com.android.server.am.proto.ProcessToGcProto;
-import com.android.server.am.proto.StickyBroadcastProto;
+import com.android.server.am.ActivityManagerServiceProto;
+import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
+import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
+import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
+import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
+import com.android.server.am.ActivityManagerServiceDumpServicesProto;
+import com.android.server.am.GrantUriProto;
+import com.android.server.am.ImportanceTokenProto;
+import com.android.server.am.MemInfoDumpProto;
+import com.android.server.am.NeededUriGrantsProto;
+import com.android.server.am.ProcessOomProto;
+import com.android.server.am.ProcessToGcProto;
+import com.android.server.am.StickyBroadcastProto;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
@@ -755,21 +754,6 @@
     private final RecentTasks mRecentTasks;
 
     /**
-     * For addAppTask: cached of the last activity component that was added.
-     */
-    ComponentName mLastAddedTaskComponent;
-
-    /**
-     * For addAppTask: cached of the last activity uid that was added.
-     */
-    int mLastAddedTaskUid;
-
-    /**
-     * For addAppTask: cached of the last ActivityInfo that was added.
-     */
-    ActivityInfo mLastAddedTaskActivity;
-
-    /**
      * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
      */
     String mDeviceOwnerName;
@@ -2867,6 +2851,7 @@
         public void onBootPhase(int phase) {
             if (phase == PHASE_SYSTEM_SERVICES_READY) {
                 mService.mBatteryStatsService.systemServicesReady();
+                mService.mServices.systemServicesReady();
             }
         }
 
@@ -9132,7 +9117,7 @@
 
     public boolean isAppStartModeDisabled(int uid, String packageName) {
         synchronized (this) {
-            return getAppStartModeLocked(uid, packageName, 0, -1, false, true)
+            return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
                     == ActivityManager.APP_START_MODE_DISABLED;
         }
     }
@@ -9208,12 +9193,12 @@
     }
 
     int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
-            int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
+            int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
         UidRecord uidRec = mActiveUids.get(uid);
         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
                 + (uidRec != null ? uidRec.idle : false));
-        if (uidRec == null || alwaysRestrict || uidRec.idle) {
+        if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
             boolean ephemeral;
             if (uidRec == null) {
                 ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
@@ -10611,27 +10596,24 @@
                         intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
                     }
                 }
-                if (!comp.equals(mLastAddedTaskComponent) || callingUid != mLastAddedTaskUid) {
-                    mLastAddedTaskActivity = null;
-                }
-                ActivityInfo ainfo = mLastAddedTaskActivity;
-                if (ainfo == null) {
-                    ainfo = mLastAddedTaskActivity = AppGlobals.getPackageManager().getActivityInfo(
-                            comp, 0, UserHandle.getUserId(callingUid));
-                    if (ainfo.applicationInfo.uid != callingUid) {
-                        throw new SecurityException(
-                                "Can't add task for another application: target uid="
-                                + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
-                    }
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp, 0,
+                        UserHandle.getUserId(callingUid));
+                if (ainfo.applicationInfo.uid != callingUid) {
+                    throw new SecurityException(
+                            "Can't add task for another application: target uid="
+                            + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
                 }
 
-                TaskRecord task = TaskRecord.create(this,
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
-                        ainfo, intent, description);
+                final ActivityStack stack = r.getStack();
+                final TaskRecord task = stack.createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
                 if (!mRecentTasks.addToBottom(task)) {
+                    // The app has too many tasks already and we can't add any more
+                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
                     return INVALID_TASK_ID;
                 }
-                r.getStack().addTask(task, !ON_TOP, "addAppTask");
+                task.lastTaskDescription.copyFrom(description);
 
                 // TODO: Send the thumbnail to WM to store it.
 
@@ -14288,14 +14270,20 @@
             return err;
         }
 
-        synchronized(this) {
-            r.requestedVrComponent = (enabled) ? packageName : null;
+        // Clear the binder calling uid since this path may call moveToTask().
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized(this) {
+                r.requestedVrComponent = (enabled) ? packageName : null;
 
-            // Update associated state if this activity is currently focused
-            if (r == mStackSupervisor.getResumedActivityLocked()) {
-                applyUpdateVrModeLocked(r);
+                // Update associated state if this activity is currently focused
+                if (r == mStackSupervisor.getResumedActivityLocked()) {
+                    applyUpdateVrModeLocked(r);
+                }
+                return 0;
             }
-            return 0;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
         }
     }
 
@@ -17104,8 +17092,8 @@
 
             if (mRunningVoice != null) {
                 final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
-                proto.write(ActivityManagerServiceDumpProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
-                mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VoiceProto.WAKELOCK);
+                proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, mRunningVoice.toString());
+                mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
                 proto.end(vrToken);
             }
 
@@ -21764,6 +21752,56 @@
     // INSTRUMENTATION
     // =========================================================
 
+    private static String[] HIDDENAPI_EXEMPT_PACKAGES = {
+        "com.android.bluetooth.tests",
+        "com.android.managedprovisioning.tests",
+        "com.android.frameworks.coretests",
+        "com.android.frameworks.coretests.binderproxycountingtestapp",
+        "com.android.frameworks.coretests.binderproxycountingtestservice",
+        "com.android.frameworks.tests.net",
+        "com.android.frameworks.tests.uiservices",
+        "com.android.coretests.apps.bstatstestapp",
+        "com.android.servicestests.apps.conntestapp",
+        "com.android.frameworks.servicestests",
+        "com.android.frameworks.utiltests",
+        "com.android.mtp.tests",
+        "android.mtp",
+        "com.android.documentsui.tests",
+        "com.android.shell.tests",
+        "com.android.systemui.tests",
+        "com.android.testables",
+        "android.net.wifi.test",
+        "com.android.server.wifi.test",
+        "com.android.frameworks.telephonytests",
+        "com.android.providers.contacts.tests",
+        "com.android.providers.contacts.tests2",
+        "com.android.settings.tests.unit",
+        "com.android.server.telecom.tests",
+        "com.android.vcard.tests",
+        "com.android.providers.blockednumber.tests",
+        "android.settings.functional",
+        "com.android.notification.functional",
+        "com.android.frameworks.dexloggertest",
+        "com.android.server.usb",
+        "com.android.providers.downloads.tests",
+        "com.android.emergency.tests.unit",
+        "com.android.providers.calendar.tests",
+        "com.android.settingslib",
+        "com.android.rs.test",
+        "com.android.printspooler.outofprocess.tests",
+        "com.android.cellbroadcastreceiver.tests.unit",
+        "com.android.providers.telephony.tests",
+        "com.android.carrierconfig.tests",
+        "com.android.phone.tests",
+        "com.android.service.ims.presence.tests",
+        "com.android.providers.setting.test",
+        "com.android.frameworks.locationtests",
+        "com.android.frameworks.coretests.privacy",
+        "com.android.settings.ui",
+        "com.android.perftests.core",
+        "com.android.perftests.multiuser",
+    };
+
     public boolean startInstrumentation(ComponentName className,
             String profileFile, int flags, Bundle arguments,
             IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
@@ -21846,6 +21884,14 @@
             }
             boolean disableHiddenApiChecks =
                     (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+
+            // TODO: Temporary whitelist of packages which need to be exempt from hidden API
+            //       checks. Remove this as soon as the testing infrastructure allows to set
+            //       the flag in AndroidTest.xml.
+            if (Arrays.asList(HIDDENAPI_EXEMPT_PACKAGES).contains(ai.packageName)) {
+                disableHiddenApiChecks = true;
+            }
+
             ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
                     abiOverride);
             app.instr = activeInstr;
@@ -26259,10 +26305,12 @@
             return mUserController.mMaxRunningUsers;
         }
 
+        @Override
         public boolean isCallerRecents(int callingUid) {
             return getRecentTasks().isCallerRecents(callingUid);
         }
 
+        @Override
         public boolean isRecentsComponentHomeActivity(int userId) {
             return getRecentTasks().isRecentsComponentHomeActivity(userId);
         }
@@ -26301,6 +26349,11 @@
             }
             return processMemoryStates;
         }
+
+        @Override
+        public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+            ActivityManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 19ca3be..ccc17a3 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -112,16 +112,16 @@
 import static com.android.server.am.TaskPersister.DEBUG;
 import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.proto.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.proto.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.proto.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.proto.ActivityRecordProto.STATE;
-import static com.android.server.am.proto.ActivityRecordProto.VISIBLE;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
-import static com.android.server.wm.proto.IdentifierProto.TITLE;
-import static com.android.server.wm.proto.IdentifierProto.USER_ID;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -2738,11 +2738,6 @@
         } else {
             service.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
             setState(PAUSED, "relaunchActivityLocked");
-            // if the app is relaunched when it's stopped, and we're not resuming,
-            // put it back into stopped state.
-            if (stopped) {
-                getStack().addToStopping(this, true /* scheduleIdle */, false /* idleDelayed */);
-            }
         }
 
         configChangeFlags = 0;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 795cb70..20b938b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -82,13 +82,13 @@
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.proto.ActivityStackProto.BOUNDS;
-import static com.android.server.am.proto.ActivityStackProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityStackProto.DISPLAY_ID;
-import static com.android.server.am.proto.ActivityStackProto.FULLSCREEN;
-import static com.android.server.am.proto.ActivityStackProto.ID;
-import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY;
-import static com.android.server.am.proto.ActivityStackProto.TASKS;
+import static com.android.server.am.ActivityStackProto.BOUNDS;
+import static com.android.server.am.ActivityStackProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackProto.DISPLAY_ID;
+import static com.android.server.am.ActivityStackProto.FULLSCREEN;
+import static com.android.server.am.ActivityStackProto.ID;
+import static com.android.server.am.ActivityStackProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityStackProto.TASKS;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_NONE;
@@ -1356,17 +1356,7 @@
             if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                     "Sleep => pause with userLeaving=false");
 
-            // If we are in the middle of resuming the top activity in
-            // {@link #resumeTopActivityUncheckedLocked}, mResumedActivity will be set but not
-            // resumed yet. We must not proceed pausing the activity here. This method will be
-            // called again if necessary as part of {@link #checkReadyForSleep} or
-            // {@link ActivityStackSupervisor#checkReadyForSleepLocked}.
-            if (mStackSupervisor.inResumeTopActivity) {
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "In the middle of resuming top activity "
-                        + mResumedActivity);
-            } else {
-                startPausingLocked(false, true, null, false);
-            }
+            startPausingLocked(false, true, null, false);
             shouldSleep = false ;
         } else if (mPausingActivity != null) {
             // Still waiting for something to pause; can't sleep yet.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 19fffbb..265e4fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -90,12 +90,12 @@
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
 import static java.lang.Integer.MAX_VALUE;
@@ -643,7 +643,7 @@
     void setWindowManager(WindowManagerService wm) {
         synchronized (mService) {
             mWindowManager = wm;
-            mKeyguardController.setWindowManager(wm);
+            getKeyguardController().setWindowManager(wm);
 
             mDisplayManager =
                     (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -1199,6 +1199,12 @@
         for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
             final int displayId = mTmpOrderedDisplayIds.get(i);
             final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+            // If WindowManagerService has encountered the display before we have, ignore as there
+            // will be no stacks present and therefore no activities.
+            if (display == null) {
+                continue;
+            }
             for (int j = display.getChildCount() - 1; j >= 0; --j) {
                 final ActivityStack stack = display.getChildAt(j);
                 if (stack != focusedStack && stack.isTopStackOnDisplay() && stack.isFocusable()) {
@@ -1312,7 +1318,7 @@
 
             r.setProcess(app);
 
-            if (mKeyguardController.isKeyguardLocked()) {
+            if (getKeyguardController().isKeyguardLocked()) {
                 r.notifyUnknownVisibilityLaunched();
             }
 
@@ -3377,7 +3383,7 @@
                 } else {
                     stack.awakeFromSleepingLocked();
                     if (isFocusedStack(stack)
-                            && !mKeyguardController.isKeyguardShowing(display.mDisplayId)) {
+                            && !getKeyguardController().isKeyguardShowing(display.mDisplayId)) {
                         // If the keyguard is unlocked - resume immediately.
                         // It is possible that the display will not be awake at the time we
                         // process the keyguard going away, which can happen before the sleep token
@@ -3501,7 +3507,7 @@
 
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        mKeyguardController.beginActivityVisibilityUpdate();
+        getKeyguardController().beginActivityVisibilityUpdate();
         try {
             // First the front stacks. In case any are not fullscreen and are in front of home.
             for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3512,7 +3518,7 @@
                 }
             }
         } finally {
-            mKeyguardController.endActivityVisibilityUpdate();
+            getKeyguardController().endActivityVisibilityUpdate();
         }
     }
 
@@ -3799,7 +3805,7 @@
         pw.print(prefix); pw.print("isHomeRecentsComponent=");
         pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
 
-        mKeyguardController.dump(pw, prefix);
+        getKeyguardController().dump(pw, prefix);
         mService.mLockTaskController.dump(pw, prefix);
     }
 
@@ -3810,7 +3816,7 @@
             ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
             activityDisplay.writeToProto(proto, DISPLAYS);
         }
-        mKeyguardController.writeToProto(proto, KEYGUARD_CONTROLLER);
+        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
         if (mFocusedStack != null) {
             proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
             ActivityRecord focusedActivity = getResumedActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bd53eac..a30a944 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1834,7 +1834,8 @@
                                     mNoAnimation, mOptions, mStartActivity.appTimeTracker,
                                     "bringToFrontInsteadOfAdjacentLaunch");
                         }
-                        mMovedToFront = true;
+                        mMovedToFront = launchStack != launchStack.getDisplay()
+                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
                     } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {
                         // Target and computed stacks are on different displays and we've
                         // found a matching task - move the existing instance to that display and
@@ -2393,6 +2394,11 @@
         return this;
     }
 
+    @VisibleForTesting
+    Intent getIntent() {
+        return mRequest.intent;
+    }
+
     ActivityStarter setReason(String reason) {
         mRequest.reason = reason;
         return this;
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index 972a692..4eaebd0 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -19,8 +19,6 @@
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.AppBindRecordProto;
-
 import java.io.PrintWriter;
 
 /**
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index ed09879..b2872e4 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -22,7 +22,6 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.server.RescueParty;
 import com.android.server.Watchdog;
-import com.android.server.am.proto.AppErrorsProto;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
diff --git a/services/core/java/com/android/server/am/AppTimeTracker.java b/services/core/java/com/android/server/am/AppTimeTracker.java
index d96364a..772865d 100644
--- a/services/core/java/com/android/server/am/AppTimeTracker.java
+++ b/services/core/java/com/android/server/am/AppTimeTracker.java
@@ -28,8 +28,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 
-import com.android.server.am.proto.AppTimeTrackerProto;
-
 import java.io.PrintWriter;
 
 /**
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1f10181..2291e44 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -283,29 +283,34 @@
                 mUseLatestStates = true;
             }
 
-            synchronized (mWorkerLock) {
-                if (DEBUG) {
-                    Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
-                }
-                try {
-                    updateExternalStatsLocked(reason, updateFlags, onBattery,
-                            onBatteryScreenOff, useLatestStates);
-                } finally {
+            try {
+                synchronized (mWorkerLock) {
                     if (DEBUG) {
-                        Slog.d(TAG, "end updateExternalStatsSync");
+                        Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
+                    }
+                    try {
+                        updateExternalStatsLocked(reason, updateFlags, onBattery,
+                                onBatteryScreenOff, useLatestStates);
+                    } finally {
+                        if (DEBUG) {
+                            Slog.d(TAG, "end updateExternalStatsSync");
+                        }
                     }
                 }
-            }
 
-            if ((updateFlags & UPDATE_CPU) != 0) {
-                mStats.copyFromAllUidsCpuTimes();
-            }
-
-            // Clean up any UIDs if necessary.
-            synchronized (mStats) {
-                for (int uid : uidsToRemove) {
-                    mStats.removeIsolatedUidLocked(uid);
+                if ((updateFlags & UPDATE_CPU) != 0) {
+                    mStats.copyFromAllUidsCpuTimes();
                 }
+
+                // Clean up any UIDs if necessary.
+                synchronized (mStats) {
+                    for (int uid : uidsToRemove) {
+                        mStats.removeIsolatedUidLocked(uid);
+                    }
+                    mStats.clearPendingRemovedUids();
+                }
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Error updating external stats: ", e);
             }
         }
     };
@@ -397,7 +402,7 @@
                 if (bluetoothInfo.isValid()) {
                     mStats.updateBluetoothStateLocked(bluetoothInfo);
                 } else {
-                    Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
+                    Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
                 }
             }
         }
@@ -409,7 +414,7 @@
             if (wifiInfo.isValid()) {
                 mStats.updateWifiState(extractDeltaLocked(wifiInfo));
             } else {
-                Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
+                Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
             }
         }
 
@@ -417,7 +422,7 @@
             if (modemInfo.isValid()) {
                 mStats.updateMobileRadioState(modemInfo);
             } else {
-                Slog.e(TAG, "modem info is invalid: " + modemInfo);
+                Slog.w(TAG, "modem info is invalid: " + modemInfo);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3db1da5..3c49ece 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1193,6 +1193,7 @@
         pw.println("  --new-daily: immediately create and write new daily stats record.");
         pw.println("  --read-daily: read-load last written daily stats.");
         pw.println("  --settings: dump the settings key/values related to batterystats");
+        pw.println("  --cpu: dump cpu stats for debugging purpose");
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -1211,6 +1212,12 @@
         }
     }
 
+    private void dumpCpuStats(PrintWriter pw) {
+        synchronized (mStats) {
+            mStats.dumpCpuStatsLocked(pw);
+        }
+    }
+
     private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
         i++;
         if (i >= args.length) {
@@ -1324,6 +1331,9 @@
                 } else if ("--settings".equals(arg)) {
                     dumpSettings(pw);
                     return;
+                } else if ("--cpu".equals(arg)) {
+                    dumpCpuStats(pw);
+                    return;
                 } else if ("-a".equals(arg)) {
                     flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 7ff227f..8e2ca06 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -21,8 +21,6 @@
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.BroadcastFilterProto;
-
 import java.io.PrintWriter;
 
 final class BroadcastFilter extends IntentFilter {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index ea90db3..cc3a887 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -55,8 +55,6 @@
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
-import com.android.server.am.proto.BroadcastQueueProto;
-
 /**
  * BROADCASTS
  *
@@ -1258,7 +1256,7 @@
             if (!skip) {
                 final int allowed = mService.getAppStartModeLocked(
                         info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                        info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false);
+                        info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
                 if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                     // We won't allow this receiver to be launched if the app has been
                     // completely disabled from launches, or it was not explicitly sent
@@ -1457,10 +1455,17 @@
             return;
         }
 
-        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r. receiver
+        // If the receiver app is being debugged we quietly ignore unresponsiveness, just
+        // tidying up and moving on to the next broadcast without crashing or ANRing this
+        // app just because it's stopped at a breakpoint.
+        final boolean debugging = (r.curApp != null && r.curApp.debugging);
+
+        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
                 + ", started " + (now - r.receiverTime) + "ms ago");
         r.receiverTime = now;
-        r.anrCount++;
+        if (!debugging) {
+            r.anrCount++;
+        }
 
         ProcessRecord app = null;
         String anrMessage = null;
@@ -1500,7 +1505,7 @@
                 r.resultExtras, r.resultAbort, false);
         scheduleBroadcastsLocked();
 
-        if (anrMessage != null) {
+        if (!debugging && anrMessage != null) {
             // Post the ANR to the handler since we do not want to process ANRs while
             // potentially holding our lock.
             mHandler.post(new AppNotResponding(app, anrMessage));
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 5b3b2a8..574ca4a 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,8 +32,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.BroadcastRecordProto;
-
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index a8e9ad9..679024ee 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -22,8 +22,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 
-import com.android.server.am.proto.ConnectionRecordProto;
-
 import java.io.PrintWriter;
 
 /**
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index 690d985..328426d 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -36,14 +36,16 @@
 
     private static final String TAG = "GlobalSettingsToPropertiesMapper";
 
+    // List mapping entries in the following format:
+    // {Settings.Global.SETTING_NAME, "system_property_name"}
+    // Important: Property being added should be whitelisted by SELinux policy or have one of the
+    // already whitelisted prefixes in system_server.te, e.g. sys.
     private static final String[][] sGlobalSettingsMapping = new String[][] {
-    //  List mapping entries in the following format:
-    //  {Settings.Global.SETTING_NAME, "system_property_name"},
         {Settings.Global.SYS_VDSO, "sys.vdso"},
         {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
         {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
         {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
-        {Settings.Global.SYS_TRACED, "persist.traced.enable"},
+        {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
     };
 
 
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index 3457a80..839b6e1 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -23,9 +23,6 @@
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.AppBindRecordProto;
-import com.android.server.am.proto.IntentBindRecordProto;
-
 import java.io.PrintWriter;
 
 /**
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 0d7eab6..b67dd0d 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -25,8 +25,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED;
-import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index ed39329..106b37f 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -62,6 +62,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
@@ -730,7 +731,29 @@
             if (isKeyguardAllowed(userId)) {
                 mWindowManager.reenableKeyguard(mToken);
             } else {
-                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+                // If keyguard is not secure and it is locked, dismiss the keyguard before
+                // disabling it, which avoids the platform to think the keyguard is still on.
+                if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
+                    mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
+                        @Override
+                        public void onDismissError() throws RemoteException {
+                            Slog.i(TAG, "setKeyguardState: failed to dismiss keyguard");
+                        }
+
+                        @Override
+                        public void onDismissSucceeded() throws RemoteException {
+                            mHandler.post(
+                                    () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
+                        }
+
+                        @Override
+                        public void onDismissCancelled() throws RemoteException {
+                            Slog.i(TAG, "setKeyguardState: dismiss cancelled");
+                        }
+                    }, null);
+                } else {
+                    mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+                }
             }
 
         } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
new file mode 100644
index 0000000..9964053
--- /dev/null
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -0,0 +1,4 @@
+per-file GlobalSettingsToPropertiesMapper.java=fkupolov@google.com
+per-file GlobalSettingsToPropertiesMapper.java=omakoto@google.com
+per-file GlobalSettingsToPropertiesMapper.java=svetoslavganov@google.com
+per-file GlobalSettingsToPropertiesMapper.java=yamasani@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0bf2691..e348bf4 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -27,7 +27,6 @@
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.proto.ProcessRecordProto;
 
 import android.app.ActivityManager;
 import android.app.Dialog;
diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java
index eee924f..5e31b2e 100644
--- a/services/core/java/com/android/server/am/ReceiverList.java
+++ b/services/core/java/com/android/server/am/ReceiverList.java
@@ -25,7 +25,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.server.IntentResolver;
-import com.android.server.am.proto.ReceiverListProto;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fc8b624..1335ced 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -508,6 +508,10 @@
                     && tr.userId == userId
                     && tr.realActivitySuspended != suspended) {
                tr.realActivitySuspended = suspended;
+               if (suspended) {
+                   mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                           REMOVE_FROM_RECENTS, "suspended-package");
+               }
                notifyTaskPersisterLocked(tr, false);
             }
         }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 49a55cb..f296c60 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,7 +19,6 @@
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.LocalServices;
-import com.android.server.am.proto.ServiceRecordProto;
 import com.android.server.notification.NotificationManagerInternal;
 
 import android.app.INotificationManager;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c7b89d3..034cb2e 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -62,19 +62,19 @@
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.proto.TaskRecordProto.ACTIVITIES;
-import static com.android.server.am.proto.TaskRecordProto.BOUNDS;
-import static com.android.server.am.proto.TaskRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.TaskRecordProto.FULLSCREEN;
-import static com.android.server.am.proto.TaskRecordProto.ID;
-import static com.android.server.am.proto.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.am.proto.TaskRecordProto.MIN_HEIGHT;
-import static com.android.server.am.proto.TaskRecordProto.MIN_WIDTH;
-import static com.android.server.am.proto.TaskRecordProto.ORIG_ACTIVITY;
-import static com.android.server.am.proto.TaskRecordProto.REAL_ACTIVITY;
-import static com.android.server.am.proto.TaskRecordProto.RESIZE_MODE;
-import static com.android.server.am.proto.TaskRecordProto.STACK_ID;
-import static com.android.server.am.proto.TaskRecordProto.ACTIVITY_TYPE;
+import static com.android.server.am.TaskRecordProto.ACTIVITIES;
+import static com.android.server.am.TaskRecordProto.BOUNDS;
+import static com.android.server.am.TaskRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.TaskRecordProto.ID;
+import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
+import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
+import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
+import static com.android.server.am.TaskRecordProto.ORIG_ACTIVITY;
+import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
+import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
+import static com.android.server.am.TaskRecordProto.STACK_ID;
+import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
 
 import static java.lang.Integer.MAX_VALUE;
 
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index d49f3ba..3b859ed 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -28,7 +28,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.am.proto.UidRecordProto;
 
 /**
  * Overall information about a uid that has actively running processes.
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index fc07c1a..8eda38e 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -22,8 +22,6 @@
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.am.proto.UriPermissionOwnerProto;
-
 import com.google.android.collect.Sets;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index af1ab83..0d125e0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -95,7 +95,6 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemServiceManager;
-import com.android.server.am.proto.UserControllerProto;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 00597e2..4f5c59c 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -27,7 +27,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ProgressReporter;
-import com.android.server.am.proto.UserStateProto;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 9d34a80..45410d79 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -24,7 +24,6 @@
 import android.util.proto.ProtoUtils;
 
 import com.android.server.LocalServices;
-import com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto.VrControllerProto;
 import com.android.server.vr.VrManagerInternal;
 
 /**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9ef84d2..041764f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -24,6 +24,9 @@
 import static android.media.AudioManager.STREAM_MUSIC;
 import static android.media.AudioManager.STREAM_SYSTEM;
 import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
+import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
+import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -108,6 +111,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
@@ -124,6 +128,7 @@
 import android.util.SparseIntArray;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
@@ -440,6 +445,12 @@
 
     // Is there a vibrator
     private final boolean mHasVibrator;
+    // Used to play vibrations
+    private Vibrator mVibrator;
+    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+            .build();
 
     // Broadcast receiver for device connections intent broadcasts
     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
@@ -697,8 +708,8 @@
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
 
-        Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-        mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
 
         // Initialize volume
         int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1);
@@ -1649,16 +1660,7 @@
 
             // Check if volume update should be send to Hearing Aid
             if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
-                synchronized (mHearingAidLock) {
-                    if (mHearingAid != null) {
-                        //hearing aid expect volume value in range -128dB to 0dB
-                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, newIndex/10,
-                                AudioSystem.DEVICE_OUT_HEARING_AID);
-                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
-                            gainDB = BT_HEARING_AID_GAIN_MIN;
-                        mHearingAid.setVolume(gainDB);
-                    }
-                }
+                setHearingAidVolume(newIndex, streamType);
             }
 
             // Check if volume update should be sent to Hdmi system audio.
@@ -1907,16 +1909,7 @@
             }
 
             if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
-                synchronized (mHearingAidLock) {
-                    if (mHearingAid != null) {
-                        //hearing aid expect volume value in range -128dB to 0dB
-                        int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10, AudioSystem.DEVICE_OUT_HEARING_AID);
-                        if (gainDB < BT_HEARING_AID_GAIN_MIN)
-                            gainDB = BT_HEARING_AID_GAIN_MIN;
-                        mHearingAid.setVolume(gainDB);
-
-                    }
-                }
+                setHearingAidVolume(index, streamType);
             }
 
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
@@ -2441,6 +2434,54 @@
         setRingerMode(ringerMode, caller, false /*external*/);
     }
 
+    public void silenceRingerModeInternal(String reason) {
+        VibrationEffect effect = null;
+        int ringerMode = AudioManager.RINGER_MODE_SILENT;
+        int toastText = 0;
+
+        int silenceRingerSetting = Settings.Secure.VOLUME_HUSH_OFF;
+        if (mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
+            silenceRingerSetting = Settings.Secure.getIntForUser(mContentResolver,
+                    Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
+                    UserHandle.USER_CURRENT);
+        }
+
+        switch(silenceRingerSetting) {
+            case VOLUME_HUSH_MUTE:
+                effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+                ringerMode = AudioManager.RINGER_MODE_SILENT;
+                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
+                break;
+            case VOLUME_HUSH_VIBRATE:
+                effect = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+                ringerMode = AudioManager.RINGER_MODE_VIBRATE;
+                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
+                break;
+        }
+        maybeVibrate(effect);
+        setRingerModeInternal(ringerMode, reason);
+        Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
+    }
+
+    private boolean maybeVibrate(VibrationEffect effect) {
+        if (!mHasVibrator) {
+            return false;
+        }
+        final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
+        if (hapticsDisabled) {
+            return false;
+        }
+
+        if (effect == null) {
+            return false;
+        }
+        mVibrator.vibrate(
+                Binder.getCallingUid(), mContext.getOpPackageName(), effect, VIBRATION_ATTRIBUTES);
+        return true;
+    }
+
     private void setRingerMode(int ringerMode, String caller, boolean external) {
         if (mUseFixedVolume || mIsSingleVolume) {
             return;
@@ -2502,14 +2543,24 @@
             mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         }
 
-        final boolean ringerModeMute = mRingerMode == AudioManager.RINGER_MODE_VIBRATE
-                || mRingerMode == AudioManager.RINGER_MODE_SILENT;
+        final int ringerMode = mRingerMode; // Read ringer mode as reading primitives is atomic
+        final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+                || ringerMode == AudioManager.RINGER_MODE_SILENT;
+        final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
+                && isBluetoothScoOn();
+        // Ask audio policy engine to force use Bluetooth SCO channel if needed
+        final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+                + "/" + Binder.getCallingPid();
+        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_VIBRATE_RINGING,
+                shouldRingSco ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE, eventSource, 0);
 
         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
             final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
+            final boolean muteAllowedBySco =
+                    !(shouldRingSco && streamType == AudioSystem.STREAM_RING);
             final boolean shouldZenMute = shouldZenMuteStream(streamType);
             final boolean shouldMute = shouldZenMute || (ringerModeMute
-                    && isStreamAffectedByRingerMode(streamType));
+                    && isStreamAffectedByRingerMode(streamType) && muteAllowedBySco);
             if (isMuted == shouldMute) continue;
             if (!shouldMute) {
                 // unmute
@@ -3150,6 +3201,8 @@
                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
                 AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource, 0);
+        // Un-mute ringtone stream volume
+        setRingerModeInt(getRingerModeInternal(), false);
     }
 
     /** @see AudioManager#isBluetoothScoOn() */
@@ -4742,7 +4795,7 @@
         }
 
         public boolean setIndex(int index, int device, String caller) {
-            boolean changed = false;
+            boolean changed;
             int oldIndex;
             synchronized (mSettingsLock) {
                 synchronized (VolumeStreamState.class) {
@@ -4759,7 +4812,7 @@
                     // - there is no volume index stored for this device on alias stream.
                     // If changing volume of current device, also change volume of current
                     // device on aliased stream
-                    final boolean currentDevice = (device == getDeviceForStream(mStreamType));
+                    final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
                     final int numStreamTypes = AudioSystem.getNumStreamTypes();
                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                         final VolumeStreamState aliasStreamState = mStreamStates[streamType];
@@ -4768,12 +4821,22 @@
                                 (changed || !aliasStreamState.hasIndexForDevice(device))) {
                             final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
                             aliasStreamState.setIndex(scaledIndex, device, caller);
-                            if (currentDevice) {
+                            if (isCurrentDevice) {
                                 aliasStreamState.setIndex(scaledIndex,
                                         getDeviceForStream(streamType), caller);
                             }
                         }
                     }
+                    // Mirror changes in SPEAKER ringtone volume on SCO when
+                    if (changed && mStreamType == AudioSystem.STREAM_RING
+                            && device == AudioSystem.DEVICE_OUT_SPEAKER) {
+                        for (int i = 0; i < mIndexMap.size(); i++) {
+                            int otherDevice = mIndexMap.keyAt(i);
+                            if ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
+                                mIndexMap.put(otherDevice, index);
+                            }
+                        }
+                    }
                 }
             }
             if (changed) {
@@ -5624,8 +5687,24 @@
                 makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
     }
 
+    private void setHearingAidVolume(int index, int streamType) {
+        synchronized (mHearingAidLock) {
+            if (mHearingAid != null) {
+                //hearing aid expect volume value in range -128dB to 0dB
+                int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10,
+                        AudioSystem.DEVICE_OUT_HEARING_AID);
+                if (gainDB < BT_HEARING_AID_GAIN_MIN)
+                    gainDB = BT_HEARING_AID_GAIN_MIN;
+                mHearingAid.setVolume(gainDB);
+            }
+        }
+    }
+
     // must be called synchronized on mConnectedDevices
     private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
+        int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
+        setHearingAidVolume(index, AudioSystem.STREAM_MUSIC);
+
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
         mConnectedDevices.put(
@@ -5870,7 +5949,8 @@
             AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
             AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
-            AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
+            AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE |
+            AudioSystem.DEVICE_OUT_HEARING_AID;
 
     // must be called before removing the device from mConnectedDevices
     // Called synchronized on mConnectedDevices
@@ -7248,6 +7328,11 @@
         }
 
         @Override
+        public void silenceRingerModeInternal(String caller) {
+            AudioService.this.silenceRingerModeInternal(caller);
+        }
+
+        @Override
         public void updateRingerModeAffectedStreamsInternal() {
             synchronized (mSettingsLock) {
                 if (updateRingerAndZenModeAffectedStreams()) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index e084ff8..d578e95 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +41,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -150,7 +154,14 @@
         update(mUsers, mApps, true);
     }
 
-    private boolean hasPermission(PackageInfo app, String permission) {
+    @VisibleForTesting
+    boolean isPreinstalledSystemApp(PackageInfo app) {
+        int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
+        return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
+    }
+
+    @VisibleForTesting
+    boolean hasPermission(PackageInfo app, String permission) {
         if (app.requestedPermissions != null) {
             for (String p : app.requestedPermissions) {
                 if (permission.equals(p)) {
@@ -166,14 +177,40 @@
     }
 
     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
-        int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
-        if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
-            return true;
-        }
+        if (isPreinstalledSystemApp(app)) return true;
         return hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
     }
 
+    private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
+        // This function defines what it means to hold the permission to use
+        // background networks.
+        return hasPermission(app, CHANGE_NETWORK_STATE)
+                || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
+                || hasPermission(app, CONNECTIVITY_INTERNAL)
+                || hasPermission(app, NETWORK_STACK)
+                // TODO : remove this check (b/31479477). Not all preinstalled apps should
+                // have access to background networks, they should just request the appropriate
+                // permission for their use case from the list above.
+                || isPreinstalledSystemApp(app);
+    }
+
+    public boolean hasUseBackgroundNetworksPermission(int uid) {
+        final String[] names = mPackageManager.getPackagesForUid(uid);
+        if (null == names || names.length == 0) return false;
+        try {
+            // Only using the first package name. There may be multiple names if multiple
+            // apps share the same UID, but in that case they also share permissions so
+            // querying with any of the names will return the same results.
+            final PackageInfo app = mPackageManager.getPackageInfo(names[0], GET_PERMISSIONS);
+            return hasUseBackgroundNetworksPermission(app);
+        } catch (NameNotFoundException e) {
+            // App not found.
+            loge("NameNotFoundException " + names[0], e);
+            return false;
+        }
+    }
+
     private int[] toIntArray(List<Integer> list) {
         int[] array = new int[list.size()];
         for (int i = 0; i < list.size(); i++) {
@@ -308,4 +345,8 @@
     private static void loge(String s) {
         Log.e(TAG, s);
     }
+
+    private static void loge(String s, Throwable e) {
+        Log.e(TAG, s, e);
+    }
 }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index cb53521..5e1afeb 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -282,6 +282,10 @@
         return mBrightnessMapper.isDefaultConfig();
     }
 
+    public BrightnessConfiguration getDefaultConfig() {
+        return mBrightnessMapper.getDefaultConfig();
+    }
+
     private boolean setDisplayPolicy(int policy) {
         if (mDisplayPolicy == policy) {
             return false;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index c0d2599..4313d17 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -206,6 +206,8 @@
     /** @return true if the current brightness config is the default one */
     public abstract boolean isDefaultConfig();
 
+    public abstract BrightnessConfiguration getDefaultConfig();
+
     public abstract void dump(PrintWriter pw);
 
     private static float normalizeAbsoluteBrightness(int brightness) {
@@ -406,6 +408,9 @@
         }
 
         @Override
+        public BrightnessConfiguration getDefaultConfig() { return null; }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("SimpleMappingStrategy");
             pw.println("  mSpline=" + mSpline);
@@ -533,6 +538,9 @@
         }
 
         @Override
+        public BrightnessConfiguration getDefaultConfig() { return mDefaultConfig; }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 171f40e..88df195 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -25,12 +25,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
+import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Environment;
 import android.os.Handler;
@@ -110,6 +112,8 @@
 
     private static final int MSG_BACKGROUND_START = 0;
     private static final int MSG_BRIGHTNESS_CHANGED = 1;
+    private static final int MSG_STOP_SENSOR_LISTENER = 2;
+    private static final int MSG_START_SENSOR_LISTENER = 3;
 
     // Lock held while accessing mEvents, is held while writing events to flash.
     private final Object mEventsLock = new Object();
@@ -127,9 +131,14 @@
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private Handler mBgHandler;
-    // mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
+
+    // mBroadcastReceiver,  mSensorListener, mSettingsObserver and mSensorRegistered
+    // should only be used on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
+    private SettingsObserver mSettingsObserver;
+    private boolean mSensorRegistered;
+
     private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
 
     // Lock held while collecting data related to brightness changes.
@@ -178,10 +187,9 @@
 
         mSensorListener = new SensorListener();
 
-
-        if (mInjector.isInteractive(mContext)) {
-            mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler);
-        }
+        mSettingsObserver = new SettingsObserver(mBgHandler);
+        mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver);
+        startSensorListener();
 
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_SHUTDOWN);
@@ -205,10 +213,11 @@
             Slog.d(TAG, "Stop");
         }
         mBgHandler.removeMessages(MSG_BACKGROUND_START);
+        stopSensorListener();
         mInjector.unregisterSensorListener(mContext, mSensorListener);
+        mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver);
         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
         mInjector.cancelIdleJob(mContext);
-        mAmbientBrightnessStatsTracker.stop();
 
         synchronized (mDataCollectionLock) {
             mStarted = false;
@@ -366,6 +375,25 @@
         }
     }
 
+    private void startSensorListener() {
+        if (!mSensorRegistered
+                && mInjector.isInteractive(mContext)
+                && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
+            mAmbientBrightnessStatsTracker.start();
+            mSensorRegistered = true;
+            mInjector.registerSensorListener(mContext, mSensorListener,
+                    mInjector.getBackgroundHandler());
+        }
+    }
+
+    private void stopSensorListener() {
+        if (mSensorRegistered) {
+            mAmbientBrightnessStatsTracker.stop();
+            mInjector.unregisterSensorListener(mContext, mSensorListener);
+            mSensorRegistered = false;
+        }
+    }
+
     private void scheduleWriteBrightnessTrackerState() {
         if (!mWriteBrightnessTrackerStateScheduled) {
             mBgHandler.post(() -> {
@@ -724,6 +752,24 @@
         }
     }
 
+    private final class SettingsObserver extends ContentObserver {
+        public SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (DEBUG) {
+                Slog.v(TAG, "settings change " + uri);
+            }
+            if (mInjector.isBrightnessModeAutomatic(mContentResolver)) {
+                mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
+            } else {
+                mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
+            }
+        }
+    }
+
     private final class Receiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -741,12 +787,9 @@
                     batteryLevelChanged(level, scale);
                 }
             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mAmbientBrightnessStatsTracker.stop();
-                mInjector.unregisterSensorListener(mContext, mSensorListener);
+                mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
-                mAmbientBrightnessStatsTracker.start();
-                mInjector.registerSensorListener(mContext, mSensorListener,
-                        mInjector.getBackgroundHandler());
+                mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
             }
         }
     }
@@ -767,6 +810,12 @@
                             values.powerBrightnessFactor, values.isUserSetBrightness,
                             values.isDefaultBrightnessConfig);
                     break;
+                case MSG_START_SENSOR_LISTENER:
+                    startSensorListener();
+                    break;
+                case MSG_STOP_SENSOR_LISTENER:
+                    stopSensorListener();
+                    break;
             }
         }
     }
@@ -801,6 +850,18 @@
             sensorManager.unregisterListener(sensorListener);
         }
 
+        public void registerBrightnessModeObserver(ContentResolver resolver,
+                ContentObserver settingsObserver) {
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.SCREEN_BRIGHTNESS_MODE),
+                    false, settingsObserver, UserHandle.USER_ALL);
+        }
+
+        public void unregisterBrightnessModeObserver(Context context,
+                ContentObserver settingsObserver) {
+            context.getContentResolver().unregisterContentObserver(settingsObserver);
+        }
+
         public void registerReceiver(Context context,
                 BroadcastReceiver receiver, IntentFilter filter) {
             context.registerReceiver(receiver, filter);
@@ -815,6 +876,12 @@
             return BackgroundThread.getHandler();
         }
 
+        public boolean isBrightnessModeAutomatic(ContentResolver resolver) {
+            return Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT)
+                    == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+        }
+
         public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
                 int userId) {
             return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9861ea7..c4b2b5e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1878,6 +1878,48 @@
         }
 
         @Override // Binder call
+        public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+                    "Permission required to read the display's brightness configuration");
+            if (userId != UserHandle.getCallingUserId()) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.INTERACT_ACROSS_USERS,
+                        "Permission required to read the display brightness"
+                                + " configuration of another user");
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int userSerial = getUserManager().getUserSerialNumber(userId);
+                synchronized (mSyncRoot) {
+                    BrightnessConfiguration config =
+                            mPersistentDataStore.getBrightnessConfiguration(userSerial);
+                    if (config == null) {
+                        config = mDisplayPowerController.getDefaultBrightnessConfiguration();
+                    }
+                    return config;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+                    "Permission required to read the display's default brightness configuration");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mSyncRoot) {
+                    return mDisplayPowerController.getDefaultBrightnessConfiguration();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setTemporaryBrightness(int brightness) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index fa39ce4..ff8b88b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -567,6 +567,10 @@
         }
     }
 
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return mAutomaticBrightnessController.getDefaultConfig();
+    }
+
     private void sendUpdatePowerState() {
         synchronized (mLock) {
             sendUpdatePowerStateLocked();
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index d30b13c..a52dd0b 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -74,6 +74,17 @@
         }
     };
 
+    /**
+     * This method is called when authentication starts.
+     */
+    public abstract void onStart();
+
+    /**
+     * This method is called when a fingerprint is authenticated or authentication is stopped
+     * (cancelled by the user, or an error such as lockout has occurred).
+     */
+    public abstract void onStop();
+
     public AuthenticationClient(Context context, long halDeviceId, IBinder token,
             IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
             boolean restricted, String owner, Bundle bundle,
@@ -220,6 +231,7 @@
             }
             result |= true; // we have a valid fingerprint, done
             resetFailedAttempts();
+            onStop();
         }
         return result;
     }
@@ -234,6 +246,7 @@
             Slog.w(TAG, "start authentication: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
+        onStart();
         try {
             final int result = daemon.authenticate(mOpId, getGroupId());
             if (result != 0) {
@@ -266,6 +279,7 @@
             return 0;
         }
 
+        onStop();
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
index 3eae157..4100a9a 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -21,6 +21,7 @@
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.media.AudioAttributes;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.VibrationEffect;
@@ -39,6 +40,11 @@
     protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
     protected static final boolean DEBUG = FingerprintService.DEBUG;
     private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
+            new AudioAttributes.Builder()
+                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                    .build();
     private final Context mContext;
     private final long mHalDeviceId;
     private final int mTargetUserId;
@@ -223,14 +229,14 @@
     public final void vibrateSuccess() {
         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
         if (vibrator != null) {
-            vibrator.vibrate(mSuccessVibrationEffect);
+            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
         }
     }
 
     public final void vibrateError() {
         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
         if (vibrator != null) {
-            vibrator.vibrate(mErrorVibrationEffect);
+            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
         }
     }
 
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ac85484..fc8aace 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -259,11 +259,6 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mActivityManager = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
                 .getService();
-        try {
-            mActivityManager.registerTaskStackListener(mTaskStackListener);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Could not register task stack listener", e);
-        }
     }
 
     @Override
@@ -859,6 +854,24 @@
                 receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
                 dialogReceiver, mStatusBarService) {
             @Override
+            public void onStart() {
+                try {
+                    mActivityManager.registerTaskStackListener(mTaskStackListener);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Could not register task stack listener", e);
+                }
+            }
+
+            @Override
+            public void onStop() {
+                try {
+                    mActivityManager.unregisterTaskStackListener(mTaskStackListener);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Could not unregister task stack listener", e);
+                }
+            }
+
+            @Override
             public int handleFailedAttempt() {
                 final int currentUser = ActivityManager.getCurrentUser();
                 mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 0b1f9a6..66817fa 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -29,6 +29,7 @@
 import android.app.job.IJobScheduler;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
+import android.app.job.JobProtoEnums;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.app.job.JobWorkItem;
@@ -880,7 +881,8 @@
             startTrackingJobLocked(jobStatus, toCancel);
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     uId, null, jobStatus.getBatteryName(),
-                    StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
+                    StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
+                    JobProtoEnums.STOP_REASON_CANCELLED);
 
             // 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/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 578a32c..a1e066e 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -95,11 +95,18 @@
     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
 
     final JobInfo job;
-    /** Uid of the package requesting this job. */
+    /**
+     * Uid of the package requesting this job.  This can differ from the "source"
+     * uid when the job was scheduled on the app's behalf, such as with the jobs
+     * that underly Sync Manager operation.
+     */
     final int callingUid;
     final int targetSdkVersion;
     final String batteryName;
 
+    /**
+     * Identity of the app in which the job is hosted.
+     */
     final String sourcePackageName;
     final int sourceUserId;
     final int sourceUid;
@@ -263,6 +270,31 @@
         return callingUid;
     }
 
+    /**
+     * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
+     *
+     * @param job The actual requested parameters for the job
+     * @param callingUid Identity of the app that is scheduling the job.  This may not be the
+     *     app in which the job is implemented; such as with sync jobs.
+     * @param targetSdkVersion The targetSdkVersion of the app in which the job will run.
+     * @param sourcePackageName The package name of the app in which the job will run.
+     * @param sourceUserId The user in which the job will run
+     * @param standbyBucket The standby bucket that the source package is currently assigned to,
+     *     cached here for speed of handling during runnability evaluations (and updated when bucket
+     *     assignments are changed)
+     * @param heartbeat Timestamp of when the job was created, in the standby-related
+     *     timebase.
+     * @param tag A string associated with the job for debugging/logging purposes.
+     * @param numFailures Count of how many times this job has requested a reschedule because
+     *     its work was not yet finished.
+     * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
+     *     is to be considered runnable
+     * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
+     *     considered overdue
+     * @param lastSuccessfulRunTime When did we last run this job to completion?
+     * @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
+     * @param internalFlags Non-API property flags about this job
+     */
     private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
             int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
@@ -399,8 +431,8 @@
     /**
      * Create a newly scheduled job.
      * @param callingUid Uid of the package that scheduled this job.
-     * @param sourcePkg Package name on whose behalf this job is scheduled. Null indicates
-     *                          the calling package is the source.
+     * @param sourcePkg Package name of the app that will actually run the job.  Null indicates
+     *     that the calling package is the source.
      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
      *     caller.
      */
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
deleted file mode 100644
index 5c9b0ea..0000000
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.content.Context;
-import android.hardware.location.IFusedLocationHardware;
-import android.location.IFusedGeofenceHardware;
-import android.util.Log;
-
-/**
- * This class was an interop layer for JVM types and the JNI code that interacted
- * with the FLP HAL implementation.
- *
- * Now, after Treble FLP & GNSS HAL simplification, it is a thin shell that acts like the
- * pre-existing cases where there was no FLP Hardware support, to keep legacy users of this
- * class operating.
- *
- * {@hide}
- * {@Deprecated}
- */
-public class FlpHardwareProvider {
-    private static FlpHardwareProvider sSingletonInstance = null;
-
-    private final static String TAG = "FlpHardwareProvider";
-
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    public static FlpHardwareProvider getInstance(Context context) {
-        if (sSingletonInstance == null) {
-            sSingletonInstance = new FlpHardwareProvider();
-            if (DEBUG) Log.d(TAG, "getInstance() created empty provider");
-        }
-        return sSingletonInstance;
-    }
-
-    private FlpHardwareProvider() {
-    }
-
-    public static boolean isSupported() {
-        if (DEBUG) Log.d(TAG, "isSupported() returning false");
-        return false;
-    }
-
-    /**
-     * Interface implementations for services built on top of this functionality.
-     */
-    public static final String LOCATION = "Location";
-
-    public IFusedLocationHardware getLocationHardware() {
-        return null;
-    }
-
-    public IFusedGeofenceHardware getGeofenceHardware() {
-        return null;
-    }
-
-    public void cleanup() {
-        if (DEBUG) Log.d(TAG, "empty cleanup()");
-    }
-}
diff --git a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java b/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
deleted file mode 100644
index a08d326..0000000
--- a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2013 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.location;
-
-import android.content.Context;
-import android.hardware.location.IFusedLocationHardware;
-import android.hardware.location.IFusedLocationHardwareSink;
-import android.location.FusedBatchOptions;
-import android.os.RemoteException;
-
-/**
- * FusedLocationHardware decorator that adds permission checking.
- * @hide
- */
-public class FusedLocationHardwareSecure extends IFusedLocationHardware.Stub {
-    private final IFusedLocationHardware mLocationHardware;
-    private final Context mContext;
-    private final String mPermissionId;
-
-    public FusedLocationHardwareSecure(
-            IFusedLocationHardware locationHardware,
-            Context context,
-            String permissionId) {
-        mLocationHardware = locationHardware;
-        mContext = context;
-        mPermissionId = permissionId;
-    }
-
-    private void checkPermissions() {
-        mContext.enforceCallingPermission(
-                mPermissionId,
-                String.format(
-                        "Permission '%s' not granted to access FusedLocationHardware",
-                        mPermissionId));
-    }
-
-    @Override
-    public void registerSink(IFusedLocationHardwareSink eventSink) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.registerSink(eventSink);
-    }
-
-    @Override
-    public void unregisterSink(IFusedLocationHardwareSink eventSink) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.unregisterSink(eventSink);
-    }
-
-    @Override
-    public int getSupportedBatchSize() throws RemoteException {
-        checkPermissions();
-        return mLocationHardware.getSupportedBatchSize();
-    }
-
-    @Override
-    public void startBatching(int id, FusedBatchOptions batchOptions) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.startBatching(id, batchOptions);
-    }
-
-    @Override
-    public void stopBatching(int id) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.stopBatching(id);
-    }
-
-    @Override
-    public void updateBatchingOptions(
-            int id,
-            FusedBatchOptions batchoOptions
-            ) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.updateBatchingOptions(id, batchoOptions);
-    }
-
-    @Override
-    public void requestBatchOfLocations(int batchSizeRequested) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.requestBatchOfLocations(batchSizeRequested);
-    }
-
-    @Override
-    public boolean supportsDiagnosticDataInjection() throws RemoteException {
-        checkPermissions();
-        return mLocationHardware.supportsDiagnosticDataInjection();
-    }
-
-    @Override
-    public void injectDiagnosticData(String data) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.injectDiagnosticData(data);
-    }
-
-    @Override
-    public boolean supportsDeviceContextInjection() throws RemoteException {
-        checkPermissions();
-        return mLocationHardware.supportsDeviceContextInjection();
-    }
-
-    @Override
-    public void injectDeviceContext(int deviceEnabledContext) throws RemoteException {
-        checkPermissions();
-        mLocationHardware.injectDeviceContext(deviceEnabledContext);
-    }
-
-    @Override
-    public void flushBatchedLocations() throws RemoteException {
-        checkPermissions();
-        mLocationHardware.flushBatchedLocations();
-    }
-
-    @Override
-    public int getVersion() throws RemoteException {
-        checkPermissions();
-        return mLocationHardware.getVersion();
-    }
-}
diff --git a/services/core/java/com/android/server/location/FusedProxy.java b/services/core/java/com/android/server/location/FusedProxy.java
deleted file mode 100644
index b90f037..0000000
--- a/services/core/java/com/android/server/location/FusedProxy.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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 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.location;
-
-import com.android.server.ServiceWatcher;
-
-import android.Manifest;
-import android.content.Context;
-import android.hardware.location.IFusedLocationHardware;
-import android.location.IFusedProvider;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Proxy that helps bind GCore FusedProvider implementations to the Fused Hardware instances.
- *
- * @hide
- */
-public final class FusedProxy {
-    private final String TAG = "FusedProxy";
-    private final ServiceWatcher mServiceWatcher;
-    private final FusedLocationHardwareSecure mLocationHardware;
-
-    /**
-     * Constructor of the class.
-     * This is private as the class follows a factory pattern for construction.
-     *
-     * @param context           The context needed for construction.
-     * @param handler           The handler needed for construction.
-     * @param locationHardware  The instance of the Fused location hardware assigned to the proxy.
-     */
-    private FusedProxy(
-            Context context,
-            Handler handler,
-            IFusedLocationHardware locationHardware,
-            int overlaySwitchResId,
-            int defaultServicePackageNameResId,
-            int initialPackageNameResId) {
-        mLocationHardware = new FusedLocationHardwareSecure(
-                locationHardware,
-                context,
-                Manifest.permission.LOCATION_HARDWARE);
-        Runnable newServiceWork = new Runnable() {
-            @Override
-            public void run() {
-                bindProvider(mLocationHardware);
-            }
-        };
-
-        // prepare the connection to the provider
-        mServiceWatcher = new ServiceWatcher(
-                context,
-                TAG,
-                "com.android.location.service.FusedProvider",
-                overlaySwitchResId,
-                defaultServicePackageNameResId,
-                initialPackageNameResId,
-                newServiceWork,
-                handler);
-    }
-
-    /**
-     * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
-     *
-     * @param context           The context needed for construction.
-     * @param handler           The handler needed for construction.
-     * @param locationHardware  The instance of the Fused location hardware assigned to the proxy.
-     *
-     * @return An instance of the proxy if it could be bound, null otherwise.
-     */
-    public static FusedProxy createAndBind(
-            Context context,
-            Handler handler,
-            IFusedLocationHardware locationHardware,
-            int overlaySwitchResId,
-            int defaultServicePackageNameResId,
-            int initialPackageNameResId) {
-        FusedProxy fusedProxy = new FusedProxy(
-                context,
-                handler,
-                locationHardware,
-                overlaySwitchResId,
-                defaultServicePackageNameResId,
-                initialPackageNameResId);
-
-        // try to bind the Fused provider
-        if (!fusedProxy.mServiceWatcher.start()) {
-            return null;
-        }
-
-        return fusedProxy;
-    }
-
-    /**
-     * Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
-     *
-     * @param locationHardware  The FusedLocationHardware instance to use for the binding operation.
-     */
-    private void bindProvider(IFusedLocationHardware locationHardware) {
-        if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                IFusedProvider provider = IFusedProvider.Stub.asInterface(binder);
-                try {
-                    provider.onFusedLocationHardwareChange(locationHardware);
-                } catch (RemoteException e) {
-                    Log.e(TAG, e.toString());
-                }
-            }
-        })) {
-            Log.e(TAG, "No instance of FusedProvider found on FusedLocationHardware connected.");
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 729ac0c..3374b30 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -423,8 +423,8 @@
     private final GnssStatusListenerHelper mListenerHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
-    private final FusedLocationListener mFusedLocationListener = new FusedLocationListener();
-    private static int sNumFusedLocationUpdatesRequests = 0;
+    private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener();
+    private final LocationChangeListener mFusedLocationListener = new FusedLocationListener();
 
     // Handler for processing events
     private Handler mHandler;
@@ -470,7 +470,7 @@
 
     // Volatile for simple inter-thread sync on these values.
     private volatile int mHardwareYear = 0;
-    private volatile String mHardwareModelName = LocationManager.GNSS_HARDWARE_MODEL_NAME_UNKNOWN;
+    private volatile String mHardwareModelName;
 
     // Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
     // stops output right at 600m/s, depriving this of the information of a device that reaches
@@ -1097,30 +1097,31 @@
 
         LocationManager locationManager = (LocationManager) mContext.getSystemService(
                 Context.LOCATION_SERVICE);
+        String provider;
+        LocationChangeListener locationListener;
 
         if (independentFromGnss) {
             // For fast GNSS TTFF
-            Location networkLocation = getLastFreshLocation(locationManager,
-                    LocationManager.NETWORK_PROVIDER);
-            if (networkLocation != null) {
-                handleUpdateLocation(networkLocation);
-                return;
-            }
-            locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER,
-                    new NetworkLocationListener(),
-                    mHandler.getLooper());
+            provider = LocationManager.NETWORK_PROVIDER;
+            locationListener = mNetworkLocationListener;
         } else {
             // For Device-Based Hybrid (E911)
-            locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
-                    LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
-                    mFusedLocationListener, mHandler.getLooper());
-            sNumFusedLocationUpdatesRequests++;
-            mHandler.postDelayed(() -> {
-                if (--sNumFusedLocationUpdatesRequests == 0) {
-                    locationManager.removeUpdates(mFusedLocationListener);
-                }
-            }, LOCATION_UPDATE_DURATION_MILLIS);
+            provider = LocationManager.FUSED_PROVIDER;
+            locationListener = mFusedLocationListener;
         }
+
+        Log.i(TAG,
+                String.format("GNSS HAL Requesting location updates from %s provider.", provider));
+        locationManager.requestLocationUpdates(provider,
+                LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
+                locationListener, mHandler.getLooper());
+        locationListener.numLocationUpdateRequest++;
+        mHandler.postDelayed(() -> {
+            if (--locationListener.numLocationUpdateRequest == 0) {
+                Log.i(TAG, String.format("Removing location updates from %s provider.", provider));
+                locationManager.removeUpdates(locationListener);
+            }
+        }, LOCATION_UPDATE_DURATION_MILLIS);
     }
 
     private void injectBestLocation(Location location) {
@@ -1721,7 +1722,9 @@
         if (mItarSpeedLimitExceeded) {
             Log.i(TAG, "Hal reported a speed in excess of ITAR limit." +
                     "  GPS/GNSS Navigation output blocked.");
-            mGnssMetrics.logReceivedLocationStatus(false);
+            if (mStarted) {
+                mGnssMetrics.logReceivedLocationStatus(false);
+            }
             return;  // No output of location allowed
         }
 
@@ -1738,14 +1741,16 @@
             Log.e(TAG, "RemoteException calling reportLocation");
         }
 
-        mGnssMetrics.logReceivedLocationStatus(hasLatLong);
-        if (hasLatLong) {
-            if (location.hasAccuracy()) {
-                mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
-            }
-            if (mTimeToFirstFix > 0) {
-                int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
-                mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
+        if (mStarted) {
+            mGnssMetrics.logReceivedLocationStatus(hasLatLong);
+            if (hasLatLong) {
+                if (location.hasAccuracy()) {
+                    mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy());
+                }
+                if (mTimeToFirstFix > 0) {
+                    int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime);
+                    mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
+                }
             }
         }
 
@@ -1754,7 +1759,9 @@
         if (mTimeToFirstFix == 0 && hasLatLong) {
             mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime);
             if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
-            mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
+            if (mStarted) {
+                mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
+            }
 
             // notify status listeners
             mListenerHelper.onFirstFix(mTimeToFirstFix);
@@ -2597,6 +2604,8 @@
     }
 
     private abstract class LocationChangeListener implements LocationListener {
+        int numLocationUpdateRequest;
+
         @Override
         public void onStatusChanged(String provider, int status, Bundle extras) {
         }
@@ -2624,7 +2633,6 @@
         @Override
         public void onLocationChanged(Location location) {
             if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
-                Log.d(TAG, "fused location listener: " + location);
                 injectBestLocation(location);
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 74ebf3e4..b5eb8bf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2033,13 +2033,7 @@
     public int[] getPendingRecoverySecretTypes() throws RemoteException {
         throw new SecurityException("Not implemented");
     }
-
-    @Override
-    public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
-            throws RemoteException {
-        mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
-    }
-
+    
     @Override
     public byte[] startRecoverySession(@NonNull String sessionId,
             @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
@@ -2051,11 +2045,13 @@
 
     @Override
     public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId,
-            @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
+            @NonNull String rootCertificateAlias, @NonNull RecoveryCertPath verifierCertPath,
+            @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
-                sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets);
+                sessionId, rootCertificateAlias, verifierCertPath, vaultParams, vaultChallenge,
+                secrets);
     }
 
     public void closeSession(@NonNull String sessionId) throws RemoteException {
@@ -2063,11 +2059,19 @@
     }
 
     @Override
+    public Map<String, String> recoverKeyChainSnapshot(
+            @NonNull String sessionId,
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
+        return mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
+                sessionId, recoveryKeyBlob, applicationKeys);
+    }
+
+    @Override
     public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
             @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
             throws RemoteException {
-        return mRecoverableKeyStoreManager.recoverKeys(
-                sessionId, recoveryKeyBlob, applicationKeys);
+        return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys);
     }
 
     @Override
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 f46657c..a87adbd 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -255,9 +255,6 @@
             }
         }
 
-        // TODO: make sure the same counter id is used during recovery and remove temporary fix.
-        counterId = 1L;
-
         byte[] vaultParams = KeySyncUtils.packVaultParams(
                 publicKey,
                 counterId,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index a7d32ed..57fb74d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -61,8 +61,6 @@
     private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
 
     private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
-    private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
-    private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
 
     /**
      * Encrypts the recovery key using both the lock screen hash and the remote storage's public
@@ -298,8 +296,12 @@
      */
     public static byte[] packVaultParams(
             PublicKey thmPublicKey, long counterId, int maxAttempts, byte[] vaultHandle) {
-        // TODO: Check if vaultHandle has exactly the length of VAULT_HANDLE_LENGTH_BYTES somewhere
-        return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES)
+        int vaultParamsLength
+                = 65 // public key
+                + 8 // counterId
+                + 4 // maxAttempts
+                + vaultHandle.length;
+        return ByteBuffer.allocate(vaultParamsLength)
                 .order(ByteOrder.LITTLE_ENDIAN)
                 .put(SecureBox.encodePublicKey(thmPublicKey))
                 .putLong(counterId)
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 20f3403..e75722a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,6 +31,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
@@ -38,19 +39,21 @@
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.RecoveryController;
+import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.KeyStore;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
+import com.android.internal.util.Preconditions;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
 import com.android.server.locksettings.recoverablekeystore.certificate.SigXml;
 import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
-import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -60,10 +63,12 @@
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
+import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
@@ -200,20 +205,25 @@
         }
         Log.i(TAG, "Updating the certificate with the new serial number " + newSerial);
 
+        // Randomly choose and validate an endpoint certificate from the list
         CertPath certPath;
+        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
         try {
             Log.d(TAG, "Getting and validating a random endpoint certificate");
-            certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT);
+            certPath = certXml.getRandomEndpointCert(rootCert);
         } catch (CertValidationException e) {
             Log.e(TAG, "Invalid endpoint cert", e);
             throw new ServiceSpecificException(
                     ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
         }
+
+        // Save the chosen and validated certificate into database
         try {
             Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
             if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
                 mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
                 mDatabase.setShouldCreateSnapshot(userId, uid, true);
+                mDatabase.setCounterId(userId, uid, new SecureRandom().nextLong());
             }
         } catch (CertificateEncodingException e) {
             Log.e(TAG, "Failed to encode CertPath", e);
@@ -237,11 +247,11 @@
             @NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile,
             @NonNull byte[] recoveryServiceSigFile)
             throws RemoteException {
-        if (recoveryServiceCertFile == null || recoveryServiceSigFile == null) {
-            Log.d(TAG, "The given cert or sig file is null");
-            throw new ServiceSpecificException(
-                    ERROR_BAD_CERTIFICATE_FORMAT, "The given cert or sig file is null.");
+        if (rootCertificateAlias == null) {
+            Log.e(TAG, "rootCertificateAlias is null");
         }
+        Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
+        Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
 
         SigXml sigXml;
         try {
@@ -253,8 +263,9 @@
                     ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
         }
 
+        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
         try {
-            sigXml.verifyFileSignature(TrustedRootCert.TRUSTED_ROOT_CERT, recoveryServiceCertFile);
+            sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
         } catch (CertValidationException e) {
             Log.d(TAG, "The signature over the cert file is invalid."
                     + " Cert: " + HexDump.toHexString(recoveryServiceCertFile)
@@ -283,11 +294,11 @@
     /**
      * Gets all data necessary to recover application keys on new device.
      *
-     * @return recovery data
+     * @return KeyChain Snapshot.
+     * @throws ServiceSpecificException if no snapshot is pending.
      * @hide
      */
-    public @NonNull
-    KeyChainSnapshot getKeyChainSnapshot()
+    public @NonNull KeyChainSnapshot getKeyChainSnapshot()
             throws RemoteException {
         checkRecoverKeyStorePermission();
         int uid = Binder.getCallingUid();
@@ -317,7 +328,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public void setServerParams(byte[] serverParams) throws RemoteException {
+    public void setServerParams(@NonNull byte[] serverParams) throws RemoteException {
         checkRecoverKeyStorePermission();
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
@@ -330,8 +341,9 @@
     /**
      * Sets the recovery status of key with {@code alias} to {@code status}.
      */
-    public void setRecoveryStatus(String alias, int status) throws RemoteException {
+    public void setRecoveryStatus(@NonNull String alias, int status) throws RemoteException {
         checkRecoverKeyStorePermission();
+        Preconditions.checkNotNull(alias, "alias is null");
         mDatabase.setRecoveryStatus(Binder.getCallingUid(), alias, status);
     }
 
@@ -356,6 +368,7 @@
             @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
             throws RemoteException {
         checkRecoverKeyStorePermission();
+        Preconditions.checkNotNull(secretTypes, "secretTypes is null");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
         long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
@@ -408,8 +421,8 @@
      * @param vaultChallenge Challenge issued by vault service.
      * @param secrets Lock-screen hashes. For now only a single secret is supported.
      * @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
-     * @deprecated Use {@link #startRecoverySessionWithCertPath(String, RecoveryCertPath, byte[],
-     *         byte[], List)} instead.
+     * @deprecated Use {@link #startRecoverySessionWithCertPath(String, String, RecoveryCertPath,
+     *         byte[], byte[], List)} instead.
      *
      * @hide
      */
@@ -449,6 +462,7 @@
                 uid,
                 new RecoverySessionStorage.Entry(sessionId, kfHash, keyClaimant, vaultParams));
 
+        Log.i(TAG, "Received VaultParams for recovery: " + HexDump.toHexString(vaultParams));
         try {
             byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(kfHash);
             return KeySyncUtils.encryptRecoveryClaim(
@@ -479,13 +493,21 @@
      */
     public @NonNull byte[] startRecoverySessionWithCertPath(
             @NonNull String sessionId,
+            @NonNull String rootCertificateAlias,
             @NonNull RecoveryCertPath verifierCertPath,
             @NonNull byte[] vaultParams,
             @NonNull byte[] vaultChallenge,
             @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-
+        if (rootCertificateAlias == null) {
+            Log.e(TAG, "rootCertificateAlias is null");
+        }
+        Preconditions.checkNotNull(sessionId, "invalid session");
+        Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
+        Preconditions.checkNotNull(vaultParams, "vaultParams is null");
+        Preconditions.checkNotNull(vaultChallenge, "vaultChallenge is null");
+        Preconditions.checkNotNull(secrets, "secrets is null");
         CertPath certPath;
         try {
             certPath = verifierCertPath.getCertPath();
@@ -495,11 +517,10 @@
         }
 
         try {
-            CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, certPath);
+            CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath);
         } catch (CertValidationException e) {
             Log.e(TAG, "Failed to validate the given cert path", e);
-            // TODO: Change this to ERROR_INVALID_CERTIFICATE once ag/3666620 is submitted
-            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage());
+            throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
         }
 
         byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
@@ -532,6 +553,9 @@
             @NonNull List<WrappedApplicationKey> applicationKeys)
             throws RemoteException {
         checkRecoverKeyStorePermission();
+        Preconditions.checkNotNull(sessionId, "invalid session");
+        Preconditions.checkNotNull(encryptedRecoveryKey, "encryptedRecoveryKey is null");
+        Preconditions.checkNotNull(applicationKeys, "encryptedRecoveryKey is null");
         int uid = Binder.getCallingUid();
         RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
         if (sessionEntry == null) {
@@ -550,6 +574,78 @@
     }
 
     /**
+     * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
+     * service.
+     *
+     * @param sessionId The session ID used to generate the claim. See
+     *     {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
+     * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault
+     *     service.
+     * @param applicationKeys The encrypted key blobs returned by the remote vault service. These
+     *     were wrapped with the recovery key.
+     * @throws RemoteException if an error occurred recovering the keys.
+     */
+    public Map<String, String> recoverKeyChainSnapshot(
+            @NonNull String sessionId,
+            @NonNull byte[] encryptedRecoveryKey,
+            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
+        checkRecoverKeyStorePermission();
+        int userId = UserHandle.getCallingUserId();
+        int uid = Binder.getCallingUid();
+        RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
+        if (sessionEntry == null) {
+            throw new ServiceSpecificException(ERROR_SESSION_EXPIRED,
+                    String.format(Locale.US,
+                            "Application uid=%d does not have pending session '%s'",
+                            uid,
+                            sessionId));
+        }
+
+        try {
+            byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
+            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys);
+            return importKeyMaterials(userId, uid, keysByAlias);
+        } catch (KeyStoreException e) {
+            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+        } finally {
+            sessionEntry.destroy();
+            mRecoverySessionStorage.remove(uid);
+        }
+    }
+
+    /**
+     * Imports the key materials, returning a map from alias to grant alias for the calling user.
+     *
+     * @param userId The calling user ID.
+     * @param uid The calling uid.
+     * @param keysByAlias The key materials, keyed by alias.
+     * @throws KeyStoreException if an error occurs importing the key or getting the grant.
+     */
+    private Map<String, String> importKeyMaterials(
+            int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException {
+        ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size());
+        for (String alias : keysByAlias.keySet()) {
+            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias));
+            String grantAlias = getAlias(userId, uid, alias);
+            Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias));
+            grantAliasesByAlias.put(alias, grantAlias);
+        }
+        return grantAliasesByAlias;
+    }
+
+    /**
+     * Returns an alias for the key.
+     *
+     * @param userId The user ID of the calling process.
+     * @param uid The uid of the calling process.
+     * @param alias The alias of the key.
+     * @return The alias in the calling process's keystore.
+     */
+    private String getAlias(int userId, int uid, String alias) {
+        return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+    }
+
+    /**
      * Deprecated
      * Generates a key named {@code alias} in the recoverable store for the calling uid. Then
      * returns the raw key material.
@@ -586,10 +682,13 @@
      * Destroys the session with the given {@code sessionId}.
      */
     public void closeSession(@NonNull String sessionId) throws RemoteException {
+        checkRecoverKeyStorePermission();
+        Preconditions.checkNotNull(sessionId, "invalid session");
         mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId);
     }
 
     public void removeKey(@NonNull String alias) throws RemoteException {
+        Preconditions.checkNotNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
@@ -607,6 +706,7 @@
      * @return grant alias, which caller can use to access the key.
      */
     public String generateKey(@NonNull String alias) throws RemoteException {
+        Preconditions.checkNotNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
@@ -626,7 +726,7 @@
             byte[] secretKey =
                     mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
-            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+            return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
         }
@@ -645,8 +745,9 @@
      */
     public String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
             throws RemoteException {
-        if (keyBytes == null ||
-                keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
+        Preconditions.checkNotNull(alias, "alias is null");
+        Preconditions.checkNotNull(keyBytes, "keyBytes is null");
+        if (keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
             Log.e(TAG, "The given key for import doesn't have the required length "
                     + RecoverableKeyGenerator.KEY_SIZE_BITS);
             throw new ServiceSpecificException(ERROR_INVALID_KEY_FORMAT,
@@ -677,7 +778,7 @@
 
             // Import the key to Android KeyStore and get grant
             mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
-            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+            return getAlias(userId, uid, alias);
         } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
         }
@@ -689,10 +790,10 @@
      * @return grant alias, which caller can use to access the key.
      */
     public String getKey(@NonNull String alias) throws RemoteException {
+        Preconditions.checkNotNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
-        return grantAlias;
+        return getAlias(userId, uid, alias);
     }
 
     private byte[] decryptRecoveryKey(
@@ -837,6 +938,21 @@
         }
     }
 
+    private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
+        if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
+            // Use the default Google Key Vault Service CA certificate if the alias is not provided
+            rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+        }
+
+        X509Certificate rootCertificate =
+                TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
+        if (rootCertificate == null) {
+            throw new ServiceSpecificException(
+                    ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
+        }
+        return rootCertificate;
+    }
+
     private void checkRecoverKeyStorePermission() {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
index 3d97623..84ddbf7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -24,6 +24,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.security.KeyStore;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
@@ -31,6 +32,8 @@
 
 import java.security.KeyStore.SecretKeyEntry;
 import java.security.KeyStoreException;
+import java.util.Locale;
+
 import javax.crypto.spec.SecretKeySpec;
 
 /**
@@ -40,11 +43,13 @@
  * revealing key material
  */
 public class ApplicationKeyStorage {
+    private static final String TAG = "RecoverableAppKeyStore";
+
     private static final String APPLICATION_KEY_ALIAS_PREFIX =
             "com.android.server.locksettings.recoverablekeystore/application/";
 
-    KeyStoreProxy mKeyStore;
-    KeyStore mKeystoreService;
+    private final KeyStoreProxy mKeyStore;
+    private final KeyStore mKeystoreService;
 
     public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
             throws KeyStoreException {
@@ -65,12 +70,15 @@
     public @Nullable String getGrantAlias(int userId, int uid, String alias) {
         // Aliases used by {@link KeyStore} are different than used by public API.
         // {@code USER_PRIVATE_KEY} prefix is used secret keys.
+        Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
         String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
         return mKeystoreService.grant(keystoreAlias, uid);
     }
 
     public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
             throws KeyStoreException {
+        Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material",
+                userId, uid, alias, secretKey.length));
         try {
             mKeyStore.setEntry(
                 getInternalAlias(userId, uid, alias),
@@ -87,6 +95,7 @@
     }
 
     public void deleteEntry(int userId, int uid, String alias) {
+        Log.i(TAG, String.format(Locale.US, "Del %d/%d/%s", userId, uid, alias));
         try {
             mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
         } catch (KeyStoreException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
deleted file mode 100644
index 52381b8..0000000
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshot.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.locksettings.recoverablekeystore.storage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class provides helper methods serialize and deserialize {@link KeyChainSnapshot}.
- *
- * <p> It is necessary since {@link android.os.Parcelable} is not designed for persistent storage.
- *
- * <p> For every list, length is stored before the elements.
- *
- */
-public class PersistentKeyChainSnapshot {
-    private static final int VERSION = 1;
-    private static final int NULL_LIST_LENGTH = -1;
-
-    private DataInputStream mInput;
-    private DataOutputStream mOut;
-    private ByteArrayOutputStream mOutStream;
-
-    @VisibleForTesting
-    PersistentKeyChainSnapshot() {
-    }
-
-    @VisibleForTesting
-    void initReader(byte[] input) {
-        mInput = new DataInputStream(new ByteArrayInputStream(input));
-    }
-
-    @VisibleForTesting
-    void initWriter() {
-        mOutStream = new ByteArrayOutputStream();
-        mOut = new DataOutputStream(mOutStream);
-    }
-
-    @VisibleForTesting
-    byte[] getOutput() {
-        return mOutStream.toByteArray();
-    }
-
-    /**
-     * Converts {@link KeyChainSnapshot} to its binary representation.
-     *
-     * @param snapshot The snapshot.
-     *
-     * @throws IOException if serialization failed.
-     */
-    public static byte[] serialize(@NonNull KeyChainSnapshot snapshot) throws IOException {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        writer.writeInt(VERSION);
-        writer.writeKeyChainSnapshot(snapshot);
-        return writer.getOutput();
-    }
-
-    /**
-     * deserializes {@link KeyChainSnapshot}.
-     *
-     * @input input - byte array produced by {@link serialize} method.
-     * @throws IOException if parsing failed.
-     */
-    public static @NonNull KeyChainSnapshot deserialize(@NonNull byte[] input)
-            throws IOException {
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(input);
-        try {
-            int version = reader.readInt();
-            if (version != VERSION) {
-                throw new IOException("Unsupported version " + version);
-            }
-            return reader.readKeyChainSnapshot();
-        } catch (IOException e) {
-            throw new IOException("Malformed KeyChainSnapshot", e);
-        }
-    }
-
-    /**
-     * Must be in sync with {@link KeyChainSnapshot.writeToParcel}
-     */
-    @VisibleForTesting
-    void writeKeyChainSnapshot(KeyChainSnapshot snapshot) throws IOException {
-        writeInt(snapshot.getSnapshotVersion());
-        writeProtectionParamsList(snapshot.getKeyChainProtectionParams());
-        writeBytes(snapshot.getEncryptedRecoveryKeyBlob());
-        writeKeysList(snapshot.getWrappedApplicationKeys());
-
-        writeInt(snapshot.getMaxAttempts());
-        writeLong(snapshot.getCounterId());
-        writeBytes(snapshot.getServerParams());
-        writeBytes(snapshot.getTrustedHardwarePublicKey());
-    }
-
-    @VisibleForTesting
-    KeyChainSnapshot readKeyChainSnapshot() throws IOException {
-        int snapshotVersion = readInt();
-        List<KeyChainProtectionParams> protectionParams = readProtectionParamsList();
-        byte[] encryptedRecoveryKey = readBytes();
-        List<WrappedApplicationKey> keysList = readKeysList();
-
-        int maxAttempts = readInt();
-        long conterId = readLong();
-        byte[] serverParams = readBytes();
-        byte[] trustedHardwarePublicKey = readBytes();
-
-        return new KeyChainSnapshot.Builder()
-                .setSnapshotVersion(snapshotVersion)
-                .setKeyChainProtectionParams(protectionParams)
-                .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
-                .setWrappedApplicationKeys(keysList)
-                .setMaxAttempts(maxAttempts)
-                .setCounterId(conterId)
-                .setServerParams(serverParams)
-                .setTrustedHardwarePublicKey(trustedHardwarePublicKey)
-                .build();
-    }
-
-    @VisibleForTesting
-    void writeProtectionParamsList(
-            @NonNull List<KeyChainProtectionParams> ProtectionParamsList) throws IOException {
-        writeInt(ProtectionParamsList.size());
-        for (KeyChainProtectionParams protectionParams : ProtectionParamsList) {
-            writeProtectionParams(protectionParams);
-        }
-    }
-
-    @VisibleForTesting
-    List<KeyChainProtectionParams> readProtectionParamsList() throws IOException {
-        int length = readInt();
-        List<KeyChainProtectionParams> result = new ArrayList<>(length);
-        for (int i = 0; i < length; i++) {
-            result.add(readProtectionParams());
-        }
-        return result;
-    }
-
-    /**
-     * Must be in sync with {@link KeyChainProtectionParams.writeToParcel}
-     */
-    @VisibleForTesting
-    void writeProtectionParams(@NonNull KeyChainProtectionParams protectionParams)
-            throws IOException {
-        if (!ArrayUtils.isEmpty(protectionParams.getSecret())) {
-            // Extra security check.
-            throw new RuntimeException("User generated secret should not be stored");
-        }
-        writeInt(protectionParams.getUserSecretType());
-        writeInt(protectionParams.getLockScreenUiFormat());
-        writeKeyDerivationParams(protectionParams.getKeyDerivationParams());
-        writeBytes(protectionParams.getSecret());
-    }
-
-    @VisibleForTesting
-    KeyChainProtectionParams readProtectionParams() throws IOException {
-        int userSecretType = readInt();
-        int lockScreenUiFormat = readInt();
-        KeyDerivationParams derivationParams = readKeyDerivationParams();
-        byte[] secret = readBytes();
-        return new KeyChainProtectionParams.Builder()
-                .setUserSecretType(userSecretType)
-                .setLockScreenUiFormat(lockScreenUiFormat)
-                .setKeyDerivationParams(derivationParams)
-                .setSecret(secret)
-                .build();
-    }
-
-    /**
-     * Must be in sync with {@link KeyDerivationParams.writeToParcel}
-     */
-    @VisibleForTesting
-    void writeKeyDerivationParams(@NonNull KeyDerivationParams Params) throws IOException {
-        writeInt(Params.getAlgorithm());
-        writeBytes(Params.getSalt());
-    }
-
-    @VisibleForTesting
-    KeyDerivationParams readKeyDerivationParams() throws IOException {
-        int algorithm = readInt();
-        byte[] salt = readBytes();
-        return KeyDerivationParams.createSha256Params(salt);
-    }
-
-    @VisibleForTesting
-    void writeKeysList(@NonNull List<WrappedApplicationKey> applicationKeys) throws IOException {
-        writeInt(applicationKeys.size());
-        for (WrappedApplicationKey keyEntry : applicationKeys) {
-            writeKeyEntry(keyEntry);
-        }
-    }
-
-    @VisibleForTesting
-    List<WrappedApplicationKey> readKeysList() throws IOException {
-        int length = readInt();
-        List<WrappedApplicationKey> result = new ArrayList<>(length);
-        for (int i = 0; i < length; i++) {
-            result.add(readKeyEntry());
-        }
-        return result;
-    }
-
-    /**
-     * Must be in sync with {@link WrappedApplicationKey.writeToParcel}
-     */
-    @VisibleForTesting
-    void writeKeyEntry(@NonNull WrappedApplicationKey keyEntry) throws IOException {
-        mOut.writeUTF(keyEntry.getAlias());
-        writeBytes(keyEntry.getEncryptedKeyMaterial());
-        writeBytes(keyEntry.getAccount());
-    }
-
-    @VisibleForTesting
-    WrappedApplicationKey readKeyEntry() throws IOException {
-        String alias = mInput.readUTF();
-        byte[] keyMaterial = readBytes();
-        byte[] account = readBytes();
-        return new WrappedApplicationKey.Builder()
-                .setAlias(alias)
-                .setEncryptedKeyMaterial(keyMaterial)
-                .setAccount(account)
-                .build();
-    }
-
-    @VisibleForTesting
-    void writeInt(int value) throws IOException {
-        mOut.writeInt(value);
-    }
-
-    @VisibleForTesting
-    int readInt() throws IOException {
-        return mInput.readInt();
-    }
-
-    @VisibleForTesting
-    void writeLong(long value) throws IOException {
-        mOut.writeLong(value);
-    }
-
-    @VisibleForTesting
-    long readLong() throws IOException {
-        return mInput.readLong();
-    }
-
-    @VisibleForTesting
-    void writeBytes(@Nullable byte[] value) throws IOException {
-        if (value == null) {
-            writeInt(NULL_LIST_LENGTH);
-            return;
-        }
-        writeInt(value.length);
-        mOut.write(value, 0, value.length);
-    }
-
-    /**
-     * Reads @code{byte[]} from current position. Converts {@code null} to an empty array.
-     */
-    @VisibleForTesting
-    @NonNull byte[] readBytes() throws IOException {
-        int length = readInt();
-        if (length == NULL_LIST_LENGTH) {
-            return new byte[]{};
-        }
-        byte[] result = new byte[length];
-        mInput.read(result, 0, result.length);
-        return result;
-    }
-}
-
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 8983ec3..2c3d3ab 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -160,7 +160,6 @@
         /**
          * Type of secret used to generate recovery key. One of
          * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
-         * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}.
          */
         static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
 
@@ -175,7 +174,7 @@
         /**
          * The algorithm used to derive cryptographic material from the key and salt. One of
          * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
-         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_ARGON2ID}.
+         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SCRYPT}.
          */
         static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3b5b1bf..7348b84 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -608,7 +608,7 @@
      */
     private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
             int resolvedUserId) {
-        if (isCurrentVolumeController(uid, pid)) return;
+        if (isCurrentVolumeController(pid, uid)) return;
         if (getContext()
                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
                     != PackageManager.PERMISSION_GRANTED
@@ -618,13 +618,13 @@
         }
     }
 
-    private boolean isCurrentVolumeController(int uid, int pid) {
+    private boolean isCurrentVolumeController(int pid, int uid) {
         return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
                 pid, uid) == PackageManager.PERMISSION_GRANTED;
     }
 
     private void enforceSystemUiPermission(String action, int pid, int uid) {
-        if (!isCurrentVolumeController(uid, pid)) {
+        if (!isCurrentVolumeController(pid, uid)) {
             throw new SecurityException("Only system ui may " + action);
         }
     }
@@ -1501,53 +1501,21 @@
          * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
          * permission or an enabled notification listener)
          *
-         * @param uid uid of the controller app
-         * @param packageName package name of the controller app
+         * @param controllerPackageName package name of the controller app
+         * @param controllerPid pid of the controller app
+         * @param controllerUid uid of the controller app
          */
         @Override
-        public boolean isTrusted(int uid, String packageName) throws RemoteException {
+        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
+                throws RemoteException {
+            final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                int userId = UserHandle.getUserId(uid);
-                // Sanity check whether uid and packageName matches
-                if (uid != mPackageManager.getPackageUid(packageName, 0, userId)) {
-                    throw new IllegalArgumentException("uid=" + uid + " and packageName="
-                            + packageName + " doesn't match");
-                }
-
-                // Check if it's system server or has MEDIA_CONTENT_CONTROL.
-                // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
-                // check here.
-                if (uid == Process.SYSTEM_UID || mPackageManager.checkPermission(
-                        android.Manifest.permission.MEDIA_CONTENT_CONTROL, packageName, uid)
-                        == PackageManager.PERMISSION_GRANTED) {
-                    return true;
-                }
-                if (DEBUG) {
-                    Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted"
-                            + " MEDIA_CONTENT_CONTROL");
-                }
-
-                // TODO(jaewan): Add hasEnabledNotificationListener(String pkgName) for
-                //               optimization (Post-P)
-                final List<ComponentName> enabledNotificationListeners =
-                        mNotificationManager.getEnabledNotificationListeners(userId);
-                if (enabledNotificationListeners != null) {
-                    for (int i = 0; i < enabledNotificationListeners.size(); i++) {
-                        if (TextUtils.equals(packageName,
-                                enabledNotificationListeners.get(i).getPackageName())) {
-                            return true;
-                        }
-                    }
-                }
+                return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
+                        controllerPid, controllerUid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
-            if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled notification"
-                        + " listener");
-            }
-            return false;
         }
 
         /**
@@ -1614,60 +1582,85 @@
             destroySession2Internal(token);
         }
 
-        // TODO(jaewan): Protect this API with permission (b/73226436)
+        // TODO(jaewan): Make this API take userId as an argument (b/73597722)
         @Override
         public List<Bundle> getSessionTokens(boolean activeSessionOnly,
-                boolean sessionServiceOnly) throws RemoteException {
+                boolean sessionServiceOnly, String packageName) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
             List<Bundle> tokens = new ArrayList<>();
-            synchronized (mLock) {
-                for (Map.Entry<SessionToken2, MediaController2> record
-                        : mSessionRecords.entrySet()) {
-                    boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
-                    boolean isActive = record.getValue() != null;
-                    if ((activeSessionOnly && !isActive)
-                            || (sessionServiceOnly && !isSessionService) ){
-                        continue;
+            try {
+                verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+                synchronized (mLock) {
+                    for (Map.Entry<SessionToken2, MediaController2> record
+                            : mSessionRecords.entrySet()) {
+                        boolean isSessionService = (record.getKey().getType() != TYPE_SESSION);
+                        boolean isActive = record.getValue() != null;
+                        if ((activeSessionOnly && !isActive)
+                                || (sessionServiceOnly && !isSessionService)) {
+                            continue;
+                        }
+                        tokens.add(record.getKey().toBundle());
                     }
-                    tokens.add(record.getKey().toBundle());
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
             return tokens;
         }
 
-        // TODO(jaewan): Protect this API with permission (b/73226436)
-        // TODO(jaewan): "userId != calling user" needs extra protection (b/73226436)
         @Override
         public void addSessionTokensListener(ISessionTokensListener listener, int userId,
-                String packageName) {
-            synchronized (mLock) {
-                final SessionTokensListenerRecord record =
-                        new SessionTokensListenerRecord(listener, userId);
-                try {
-                    listener.asBinder().linkToDeath(record, 0);
-                } catch (RemoteException e) {
+                String packageName) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid);
+                synchronized (mLock) {
+                    final SessionTokensListenerRecord record =
+                            new SessionTokensListenerRecord(listener, resolvedUserId);
+                    try {
+                        listener.asBinder().linkToDeath(record, 0);
+                    } catch (RemoteException e) {
+                    }
+                    mSessionTokensListeners.add(record);
                 }
-                mSessionTokensListeners.add(record);
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
-        // TODO(jaewan): Protect this API with permission (b/73226436)
+        // TODO(jaewan): Make this API take userId as an argument (b/73597722)
         @Override
-        public void removeSessionTokensListener(ISessionTokensListener listener) {
-            synchronized (mLock) {
-                IBinder listenerBinder = listener.asBinder();
-                for (SessionTokensListenerRecord record : mSessionTokensListeners) {
-                    if (listenerBinder.equals(record.mListener.asBinder())) {
-                        try {
-                            listenerBinder.unlinkToDeath(record, 0);
-                        } catch (NoSuchElementException e) {
+        public void removeSessionTokensListener(ISessionTokensListener listener,
+                String packageName) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid);
+                synchronized (mLock) {
+                    IBinder listenerBinder = listener.asBinder();
+                    for (SessionTokensListenerRecord record : mSessionTokensListeners) {
+                        if (listenerBinder.equals(record.mListener.asBinder())) {
+                            try {
+                                listenerBinder.unlinkToDeath(record, 0);
+                            } catch (NoSuchElementException e) {
+                            }
+                            mSessionTokensListeners.remove(record);
+                            break;
                         }
-                        mSessionTokensListeners.remove(record);
-                        break;
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
+        // For MediaSession
         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
                 final int uid) {
             String packageName = null;
@@ -1687,6 +1680,66 @@
             return resolvedUserId;
         }
 
+        // For MediaSession2
+        private int verifySessionsRequest2(int targetUserId, String callerPackageName,
+                int callerPid, int callerUid) throws RemoteException {
+            // Check that they can make calls on behalf of the user and get the final user id.
+            int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid,
+                    targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens",
+                    callerPackageName);
+            // Check if they have the permissions or their component is
+            // enabled for the user they're calling from.
+            if (!hasMediaControlPermission(
+                    resolvedUserId, callerPackageName, callerPid, callerUid)) {
+                throw new SecurityException("Missing permission to control media.");
+            }
+            return resolvedUserId;
+        }
+
+        // For MediaSession2
+        private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
+                int pid, int uid) throws RemoteException {
+            // Allow API calls from the System UI
+            if (isCurrentVolumeController(pid, uid)) {
+                return true;
+            }
+
+            // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+            // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+            // check here.
+            if (uid == Process.SYSTEM_UID || getContext().checkPermission(
+                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            } else if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+            }
+
+            // You may not access another user's content as an enabled listener.
+            final int userId = UserHandle.getUserId(uid);
+            if (resolvedUserId != userId) {
+                return false;
+            }
+
+            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
+            //               String pkgName) to notification team for optimization
+            final List<ComponentName> enabledNotificationListeners =
+                    mNotificationManager.getEnabledNotificationListeners(userId);
+            if (enabledNotificationListeners != null) {
+                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
+                    if (TextUtils.equals(packageName,
+                            enabledNotificationListeners.get(i).getPackageName())) {
+                        return true;
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+                        + "notification listener");
+            }
+            return false;
+        }
+
         private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
             MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
                     : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b911c7b..27eeb93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -593,7 +593,7 @@
         out.startDocument(null, true);
         out.startTag(null, TAG_NOTIFICATION_POLICY);
         out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
-        mZenModeHelper.writeXml(out, forBackup);
+        mZenModeHelper.writeXml(out, forBackup, null);
         mRankingHelper.writeXml(out, forBackup);
         mListeners.writeXml(out, forBackup);
         mAssistants.writeXml(out, forBackup);
@@ -959,6 +959,8 @@
             boolean queryRemove = false;
             boolean packageChanged = false;
             boolean cancelNotifications = true;
+            boolean hideNotifications = false;
+            boolean unhideNotifications = false;
             int reason = REASON_PACKAGE_CHANGED;
 
             if (action.equals(Intent.ACTION_PACKAGE_ADDED)
@@ -967,7 +969,8 @@
                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
-                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
+                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)
+                    || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
                 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                         UserHandle.USER_ALL);
                 String pkgList[] = null;
@@ -980,7 +983,12 @@
                     uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    reason = REASON_PACKAGE_SUSPENDED;
+                    cancelNotifications = false;
+                    hideNotifications = true;
+                } else if (action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    cancelNotifications = false;
+                    unhideNotifications = true;
                 } else if (queryRestart) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                     uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
@@ -1022,9 +1030,15 @@
                         if (cancelNotifications) {
                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
                                     !queryRestart, changeUserId, reason, null);
+                        } else if (hideNotifications) {
+                            hideNotificationsForPackages(pkgList);
+                        } else if (unhideNotifications) {
+                            unhideNotificationsForPackages(pkgList);
                         }
+
                     }
                 }
+
                 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
                 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
                 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
@@ -1294,7 +1308,8 @@
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
             NotificationUsageStats usageStats, AtomicFile policyFile,
-            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) {
+            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
+            UsageStatsManagerInternal appUsageStats) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1307,7 +1322,7 @@
         mPackageManagerClient = packageManagerClient;
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
-        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+        mAppUsageStats = appUsageStats;
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
@@ -1449,7 +1464,8 @@
                 null, snoozeHelper, new NotificationUsageStats(getContext()),
                 new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
-                getGroupHelper(), ActivityManager.getService());
+                getGroupHelper(), ActivityManager.getService(),
+                LocalServices.getService(UsageStatsManagerInternal.class));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -1477,6 +1493,7 @@
 
         IntentFilter suspendedPkgFilter = new IntentFilter();
         suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+        suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
         getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
                 suspendedPkgFilter, null, null);
 
@@ -2484,6 +2501,7 @@
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+
                     if (keys != null) {
                         final int N = keys.length;
                         for (int i = 0; i < N; i++) {
@@ -4269,6 +4287,14 @@
         }
     }
 
+    @GuardedBy("mNotificationLock")
+    private boolean isPackageSuspendedLocked(NotificationRecord r) {
+        final String pkg = r.sbn.getPackageName();
+        final int callingUid = r.sbn.getUid();
+
+        return isPackageSuspendedForUser(pkg, callingUid);
+    }
+
     protected class PostNotificationRunnable implements Runnable {
         private final String key;
 
@@ -4293,6 +4319,8 @@
                         Slog.i(TAG, "Cannot find enqueued record for key: " + key);
                         return;
                     }
+
+                    r.setHidden(isPackageSuspendedLocked(r));
                     NotificationRecord old = mNotificationsByKey.get(key);
                     final StatusBarNotification n = r.sbn;
                     final Notification notification = n.getNotification();
@@ -4300,6 +4328,7 @@
                     if (index < 0) {
                         mNotificationList.add(r);
                         mUsageStats.registerPostedByApp(r);
+                        r.setInterruptive(true);
                     } else {
                         old = mNotificationList.get(index);
                         mNotificationList.set(index, r);
@@ -4310,6 +4339,7 @@
                         // revoke uri permissions for changed uris
                         revokeUriPermissions(r, old);
                         r.isUpdate = true;
+                        r.setInterruptive(isVisuallyInterruptive(old, r));
                     }
 
                     mNotificationsByKey.put(n.getKey(), r);
@@ -4343,7 +4373,7 @@
                     } else {
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
                         if (old != null && !old.isCanceled) {
-                            mListeners.notifyRemovedLocked(n,
+                            mListeners.notifyRemovedLocked(r,
                                     NotificationListenerService.REASON_ERROR, null);
                             mHandler.post(new Runnable() {
                                 @Override
@@ -4359,7 +4389,9 @@
                                 + n.getPackageName());
                     }
 
-                    buzzBeepBlinkLocked(r);
+                    if (!r.isHidden()) {
+                        buzzBeepBlinkLocked(r);
+                    }
                     maybeRecordInterruptionLocked(r);
                 } finally {
                     int N = mEnqueuedNotifications.size();
@@ -4376,6 +4408,52 @@
     }
 
     /**
+     * If the notification differs enough visually, consider it a new interruptive notification.
+     */
+    @GuardedBy("mNotificationLock")
+    @VisibleForTesting
+    protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+        Notification oldN = old.sbn.getNotification();
+        Notification newN = r.sbn.getNotification();
+        if (oldN.extras == null || newN.extras == null) {
+            return false;
+        }
+        if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
+                newN.extras.get(Notification.EXTRA_TITLE))) {
+            return true;
+        }
+        if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TEXT),
+                newN.extras.get(Notification.EXTRA_TEXT))) {
+            return true;
+        }
+        if (oldN.extras.containsKey(Notification.EXTRA_PROGRESS) && newN.hasCompletedProgress()) {
+            return true;
+        }
+        // Actions
+        if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
+            return true;
+        }
+
+        try {
+            Notification.Builder oldB = Notification.Builder.recoverBuilder(getContext(), oldN);
+            Notification.Builder newB = Notification.Builder.recoverBuilder(getContext(), newN);
+
+            // Style based comparisons
+            if (Notification.areStyledNotificationsVisiblyDifferent(oldB, newB)) {
+                return true;
+            }
+
+            // Remote views
+            if (Notification.areRemoteViewsChanged(oldB, newB)) {
+                return true;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "error recovering builder", e);
+        }
+        return false;
+    }
+
+    /**
      * Keeps the last 5 packages that have notified, by user.
      */
     @GuardedBy("mNotificationLock")
@@ -4972,7 +5050,7 @@
 
     private void handleSendRankingUpdate() {
         synchronized (mNotificationLock) {
-            mListeners.notifyRankingUpdateLocked();
+            mListeners.notifyRankingUpdateLocked(null);
         }
     }
 
@@ -5157,7 +5235,7 @@
                 if (reason != REASON_SNOOZED) {
                     r.isCanceled = true;
                 }
-                mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
+                mListeners.notifyRemovedLocked(r, reason, r.getStats());
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
@@ -5266,6 +5344,7 @@
             final String pkg, final String tag, final int id,
             final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
             final int userId, final int reason, final ManagedServiceInfo listener) {
+
         // In enqueueNotificationInternal notifications are added by scheduling the
         // work on the worker handler. Hence, we also schedule the cancel on this
         // handler to avoid a scenario where an add notification call followed by a
@@ -5657,6 +5736,42 @@
         return -1;
     }
 
+    @VisibleForTesting
+    protected void hideNotificationsForPackages(String[] pkgs) {
+        synchronized (mNotificationLock) {
+            List<String> pkgList = Arrays.asList(pkgs);
+            List<NotificationRecord> changedNotifications = new ArrayList<>();
+            int numNotifications = mNotificationList.size();
+            for (int i = 0; i < numNotifications; i++) {
+                NotificationRecord rec = mNotificationList.get(i);
+                if (pkgList.contains(rec.sbn.getPackageName())) {
+                    rec.setHidden(true);
+                    changedNotifications.add(rec);
+                }
+            }
+
+            mListeners.notifyHiddenLocked(changedNotifications);
+        }
+    }
+
+    @VisibleForTesting
+    protected void unhideNotificationsForPackages(String[] pkgs) {
+        synchronized (mNotificationLock) {
+            List<String> pkgList = Arrays.asList(pkgs);
+            List<NotificationRecord> changedNotifications = new ArrayList<>();
+            int numNotifications = mNotificationList.size();
+            for (int i = 0; i < numNotifications; i++) {
+                NotificationRecord rec = mNotificationList.get(i);
+                if (pkgList.contains(rec.sbn.getPackageName())) {
+                    rec.setHidden(false);
+                    changedNotifications.add(rec);
+                }
+            }
+
+            mListeners.notifyUnhiddenLocked(changedNotifications);
+        }
+    }
+
     private void updateNotificationPulse() {
         synchronized (mNotificationLock) {
             updateLightsLocked();
@@ -5776,6 +5891,7 @@
         Bundle snoozeCriteria = new Bundle();
         Bundle showBadge = new Bundle();
         Bundle userSentiment = new Bundle();
+        Bundle hidden = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -5802,6 +5918,7 @@
             snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
             showBadge.putBoolean(key, record.canShowBadge());
             userSentiment.putInt(key, record.getUserSentiment());
+            hidden.putBoolean(key, record.isHidden());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -5812,7 +5929,7 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -6101,6 +6218,16 @@
          */
         @GuardedBy("mNotificationLock")
         public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
+            notifyPostedLocked(r, oldSbn, true);
+        }
+
+        /**
+         * @param notifyAllListeners notifies all listeners if true, else only notifies listeners
+         *                           targetting <= O_MR1
+         */
+        @GuardedBy("mNotificationLock")
+        private void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn,
+                boolean notifyAllListeners) {
             // Lazily initialized snapshots of the notification.
             StatusBarNotification sbn = r.sbn;
             TrimCache trimCache = new TrimCache(sbn);
@@ -6114,6 +6241,21 @@
                 if (!oldSbnVisible && !sbnVisible) {
                     continue;
                 }
+
+                // If the notification is hidden, don't notifyPosted listeners targeting < P.
+                // Instead, those listeners will receive notifyPosted when the notification is
+                // unhidden.
+                if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
+                    continue;
+                }
+
+                // If we shouldn't notify all listeners, this means the hidden state of
+                // a notification was changed.  Don't notifyPosted listeners targeting >= P.
+                // Instead, those listeners will receive notifyRankingUpdate.
+                if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+                    continue;
+                }
+
                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
 
                 // This notification became invisible -> remove the old one.
@@ -6168,8 +6310,9 @@
          * asynchronously notify all listeners about a removed notification
          */
         @GuardedBy("mNotificationLock")
-        public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
+        public void notifyRemovedLocked(NotificationRecord r, int reason,
                 NotificationStats notificationStats) {
+            final StatusBarNotification sbn = r.sbn;
             // make a copy in case changes are made to the underlying Notification object
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
             // notification
@@ -6178,6 +6321,21 @@
                 if (!isVisibleToListener(sbn, info)) {
                     continue;
                 }
+
+                // don't notifyRemoved for listeners targeting < P
+                // if not for reason package suspended
+                if (r.isHidden() && reason != REASON_PACKAGE_SUSPENDED
+                        && info.targetSdkVersion < Build.VERSION_CODES.P) {
+                    continue;
+                }
+
+                // don't notifyRemoved for listeners targeting >= P
+                // if the reason is package suspended
+                if (reason == REASON_PACKAGE_SUSPENDED
+                        && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+                    continue;
+                }
+
                 // Only assistants can get stats
                 final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
                         ? notificationStats : null;
@@ -6192,21 +6350,44 @@
         }
 
         /**
-         * asynchronously notify all listeners about a reordering of notifications
+         * Asynchronously notify all listeners about a reordering of notifications
+         * unless changedHiddenNotifications is populated.
+         * If changedHiddenNotifications is populated, there was a change in the hidden state
+         * of the notifications.  In this case, we only send updates to listeners that
+         * target >= P.
          */
         @GuardedBy("mNotificationLock")
-        public void notifyRankingUpdateLocked() {
+        public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
+            boolean isHiddenRankingUpdate = changedHiddenNotifications != null
+                    && changedHiddenNotifications.size() > 0;
+
             for (final ManagedServiceInfo serviceInfo : getServices()) {
                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
                     continue;
                 }
-                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyRankingUpdate(serviceInfo, update);
+
+                boolean notifyThisListener = false;
+                if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
+                        Build.VERSION_CODES.P) {
+                    for (NotificationRecord rec : changedHiddenNotifications) {
+                        if (isVisibleToListener(rec.sbn, serviceInfo)) {
+                            notifyThisListener = true;
+                            break;
+                        }
                     }
-                });
+                }
+
+                if (notifyThisListener || !isHiddenRankingUpdate) {
+                    final NotificationRankingUpdate update = makeRankingUpdateLocked(
+                            serviceInfo);
+
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            notifyRankingUpdate(serviceInfo, update);
+                        }
+                    });
+                }
             }
         }
 
@@ -6225,6 +6406,52 @@
             }
         }
 
+        /**
+         * asynchronously notify relevant listeners their notification is hidden
+         * NotificationListenerServices that target P+:
+         *      NotificationListenerService#notifyRankingUpdateLocked()
+         * NotificationListenerServices that target <= P:
+         *      NotificationListenerService#notifyRemovedLocked() with REASON_PACKAGE_SUSPENDED.
+         */
+        @GuardedBy("mNotificationLock")
+        public void notifyHiddenLocked(List<NotificationRecord> changedNotifications) {
+            if (changedNotifications == null || changedNotifications.size() == 0) {
+                return;
+            }
+
+            notifyRankingUpdateLocked(changedNotifications);
+
+            // for listeners that target < P, notifyRemoveLocked
+            int numChangedNotifications = changedNotifications.size();
+            for (int i = 0; i < numChangedNotifications; i++) {
+                NotificationRecord rec = changedNotifications.get(i);
+                mListeners.notifyRemovedLocked(rec, REASON_PACKAGE_SUSPENDED, rec.getStats());
+            }
+        }
+
+        /**
+         * asynchronously notify relevant listeners their notification is unhidden
+         * NotificationListenerServices that target P+:
+         *      NotificationListenerService#notifyRankingUpdateLocked()
+         * NotificationListenerServices that target <= P:
+         *      NotificationListeners#notifyPostedLocked()
+         */
+        @GuardedBy("mNotificationLock")
+        public void notifyUnhiddenLocked(List<NotificationRecord> changedNotifications) {
+            if (changedNotifications == null || changedNotifications.size() == 0) {
+                return;
+            }
+
+            notifyRankingUpdateLocked(changedNotifications);
+
+            // for listeners that target < P, notifyPostedLocked
+            int numChangedNotifications = changedNotifications.size();
+            for (int i = 0; i < numChangedNotifications; i++) {
+                NotificationRecord rec = changedNotifications.get(i);
+                mListeners.notifyPostedLocked(rec, rec.sbn, false);
+            }
+        }
+
         public void notifyInterruptionFilterChanged(final int interruptionFilter) {
             for (final ManagedServiceInfo serviceInfo : getServices()) {
                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
@@ -6453,6 +6680,22 @@
         }
     }
 
+    @VisibleForTesting
+    protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
+        // only use for testing: mimic receive broadcast that package is (un)suspended
+        // but does not actually (un)suspend the package
+        final Bundle extras = new Bundle();
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+                new String[]{pkg});
+
+        final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
+            : Intent.ACTION_PACKAGES_UNSUSPENDED;
+        final Intent intent = new Intent(action);
+        intent.putExtras(extras);
+
+        mPackageIntentReceiver.onReceive(getContext(), intent);
+    }
+
     /**
      * Wrapper for a StatusBarNotification object that allows transfer across a oneway
      * binder without sending large amounts of data over a oneway transaction.
@@ -6481,7 +6724,9 @@
                 + "allow_assistant COMPONENT\n"
                 + "remove_assistant COMPONENT\n"
                 + "allow_dnd PACKAGE\n"
-                + "disallow_dnd PACKAGE";
+                + "disallow_dnd PACKAGE\n"
+                + "suspend_package PACKAGE\n"
+                + "unsuspend_package PACKAGE";
 
         @Override
         public int onCommand(String cmd) {
@@ -6550,7 +6795,16 @@
                         getBinderService().setNotificationAssistantAccessGranted(cn, false);
                     }
                     break;
-
+                    case "suspend_package": {
+                        // only use for testing
+                        simulatePackageSuspendBroadcast(true, getNextArgRequired());
+                    }
+                    break;
+                    case "unsuspend_package": {
+                        // only use for testing
+                        simulatePackageSuspendBroadcast(false, getNextArgRequired());
+                    }
+                    break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f1908bf..c887085 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -103,6 +103,9 @@
     // is this notification currently being intercepted by Zen Mode?
     private boolean mIntercept;
 
+    // is this notification hidden since the app pkg is suspended?
+    private boolean mHidden;
+
     // The timestamp used for ranking.
     private long mRankingTimeMs;
 
@@ -353,6 +356,7 @@
         mPackagePriority = previous.mPackagePriority;
         mPackageVisibility = previous.mPackageVisibility;
         mIntercept = previous.mIntercept;
+        mHidden = previous.mHidden;
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
         mCreationTimeMs = previous.mCreationTimeMs;
         mVisibleSinceMs = previous.mVisibleSinceMs;
@@ -498,6 +502,7 @@
                 + NotificationListenerService.Ranking.importanceToString(mImportance));
         pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
         pw.println(prefix + "mIntercept=" + mIntercept);
+        pw.println(prefix + "mHidden==" + mHidden);
         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
         pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
@@ -702,6 +707,15 @@
         return mIntercept;
     }
 
+    public void setHidden(boolean hidden) {
+        mHidden = hidden;
+    }
+
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+
     public void setSuppressedVisualEffects(int effects) {
         mSuppressedVisualEffects = effects;
     }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b280bde..f163113 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -29,18 +29,23 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.Signature;
+import android.content.res.Resources;
 import android.metrics.LogMaker;
 import android.os.Build;
 import android.os.UserHandle;
+import android.print.PrintManager;
 import android.provider.Settings.Secure;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.RankingHelperProto;
 import android.service.notification.RankingHelperProto.RecordProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
@@ -95,12 +100,18 @@
     private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
     private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
     private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
+    private final ArrayMap<Pair<String, Integer>, Boolean> mSystemAppCache = new ArrayMap<>();
 
     private final Context mContext;
     private final RankingHandler mRankingHandler;
     private final PackageManager mPm;
     private SparseBooleanArray mBadgingEnabled;
 
+    private Signature[] mSystemSignature;
+    private String mPermissionControllerPackageName;
+    private String mServicesSystemSharedLibPackageName;
+    private String mSharedSystemSharedLibPackageName;
+
     public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
         mContext = context;
@@ -130,6 +141,8 @@
                 Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
             }
         }
+
+        getSignatures();
     }
 
     @SuppressWarnings("unchecked")
@@ -571,7 +584,7 @@
         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
             throw new IllegalArgumentException("Reserved id");
         }
-
+        final boolean isSystemApp = isSystemPackage(pkg, uid);
         NotificationChannel existing = r.channels.get(channel.getId());
         // Keep most of the existing settings
         if (existing != null && fromTargetApp) {
@@ -597,6 +610,11 @@
                 existing.setImportance(channel.getImportance());
             }
 
+            // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
+            if (existing.getUserLockedFields() == 0 & isSystemApp) {
+                existing.setBypassDnd(channel.canBypassDnd());
+            }
+
             updateConfig();
             return;
         }
@@ -604,9 +622,12 @@
                 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
             throw new IllegalArgumentException("Invalid importance level");
         }
+
         // Reset fields that apps aren't allowed to set.
-        if (fromTargetApp) {
+        if (fromTargetApp && !isSystemApp) {
             channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+        }
+        if (fromTargetApp) {
             channel.setLockscreenVisibility(r.visibility);
         }
         clearLockedFields(channel);
@@ -616,6 +637,7 @@
         if (!r.showBadge) {
             channel.setShowBadge(false);
         }
+
         r.channels.put(channel.getId(), channel);
         MetricsLogger.action(getChannelLog(channel, pkg).setType(
                 MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -625,6 +647,65 @@
         channel.unlockFields(channel.getUserLockedFields());
     }
 
+    /**
+     * Determine whether a package is a "system package", in which case certain things (like
+     * bypassing DND) should be allowed.
+     */
+    private boolean isSystemPackage(String pkg, int uid) {
+        Pair<String, Integer> app = new Pair(pkg, uid);
+        if (mSystemAppCache.containsKey(app)) {
+            return mSystemAppCache.get(app);
+        }
+
+        PackageInfo pi;
+        try {
+            pi = mPm.getPackageInfoAsUser(
+                    pkg, PackageManager.GET_SIGNATURES, UserHandle.getUserId(uid));
+        } catch (NameNotFoundException e) {
+            Slog.w(TAG, "Can't find pkg", e);
+            return false;
+        }
+        boolean isSystem = (mSystemSignature[0] != null
+                && mSystemSignature[0].equals(getFirstSignature(pi)))
+                || pkg.equals(mPermissionControllerPackageName)
+                || pkg.equals(mServicesSystemSharedLibPackageName)
+                || pkg.equals(mSharedSystemSharedLibPackageName)
+                || pkg.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
+                || isDeviceProvisioningPackage(pkg);
+        mSystemAppCache.put(app, isSystem);
+        return isSystem;
+    }
+
+    private Signature getFirstSignature(PackageInfo pkg) {
+        if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
+            return pkg.signatures[0];
+        }
+        return null;
+    }
+
+    private Signature getSystemSignature() {
+        try {
+            final PackageInfo sys = mPm.getPackageInfoAsUser(
+                    "android", PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
+            return getFirstSignature(sys);
+        } catch (NameNotFoundException e) {
+        }
+        return null;
+    }
+
+    private boolean isDeviceProvisioningPackage(String packageName) {
+        String deviceProvisioningPackage = mContext.getResources().getString(
+                com.android.internal.R.string.config_deviceProvisioningPackage);
+        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+    }
+
+    private void getSignatures() {
+        mSystemSignature = new Signature[]{getSystemSignature()};
+        mPermissionControllerPackageName = mPm.getPermissionControllerPackageName();
+        mServicesSystemSharedLibPackageName = mPm.getServicesSystemSharedLibraryPackageName();
+        mSharedSystemSharedLibPackageName = mPm.getSharedSystemSharedLibraryPackageName();
+    }
+
     @Override
     public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
             boolean fromUser) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index e862530..5c82343 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -56,6 +56,7 @@
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.notification.ZenModeProto;
 import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -234,25 +235,12 @@
             config = mDefaultConfig.copy();
             config.user = user;
         }
-        enforceDefaultRulesExist(config);
         synchronized (mConfig) {
             setConfigLocked(config, reason);
         }
         cleanUpZenRules();
     }
 
-    private void enforceDefaultRulesExist(ZenModeConfig config) {
-        for (String id : ZenModeConfig.DEFAULT_RULE_IDS) {
-            if (!config.automaticRules.containsKey(id)) {
-                if (id.equals(ZenModeConfig.EVENTS_DEFAULT_RULE_ID)) {
-                    appendDefaultEventRules(config);
-                } else if (id.equals(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID)) {
-                    appendDefaultEveryNightRule(config);
-                }
-            }
-        }
-    }
-
     public int getZenModeListenerInterruptionFilter() {
         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
     }
@@ -623,43 +611,59 @@
 
     public void readXml(XmlPullParser parser, boolean forRestore)
             throws XmlPullParserException, IOException {
-        final ZenModeConfig config = ZenModeConfig.readXml(parser);
+        ZenModeConfig config = ZenModeConfig.readXml(parser);
+        String reason = "readXml";
+
         if (config != null) {
-            if (config.version < ZenModeConfig.XML_VERSION || forRestore) {
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
-            }
             if (forRestore) {
                 //TODO: http://b/22388012
                 if (config.user != UserHandle.USER_SYSTEM) {
                     return;
                 }
                 config.manualRule = null;  // don't restore the manual rule
-                long time = System.currentTimeMillis();
-                if (config.automaticRules != null) {
-                    for (ZenRule automaticRule : config.automaticRules.values()) {
+            }
+
+            boolean resetToDefaultRules = true;
+            long time = System.currentTimeMillis();
+            if (config.automaticRules != null && config.automaticRules.size() > 0) {
+                for (ZenRule automaticRule : config.automaticRules.values()) {
+                    if (forRestore) {
                         // don't restore transient state from restored automatic rules
                         automaticRule.snoozing = false;
                         automaticRule.condition = null;
                         automaticRule.creationTime = time;
                     }
+                    resetToDefaultRules &= !automaticRule.enabled;
                 }
             }
-            if (DEBUG) Log.d(TAG, "readXml");
+
+            if (config.version < ZenModeConfig.XML_VERSION || forRestore) {
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+
+                // resets zen automatic rules to default
+                // if all prev auto rules were disabled on update
+                if (resetToDefaultRules) {
+                    config.automaticRules = new ArrayMap<>();
+                    appendDefaultRules(config);
+                    reason += ", reset to default rules";
+                }
+            }
+            if (DEBUG) Log.d(TAG, reason);
             synchronized (mConfig) {
-                setConfigLocked(config, "readXml");
+                setConfigLocked(config, reason);
             }
         }
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
+    public void writeXml(XmlSerializer out, boolean forBackup, Integer version) throws IOException {
         final int N = mConfigs.size();
         for (int i = 0; i < N; i++) {
             //TODO: http://b/22388012
             if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_SYSTEM) {
                 continue;
             }
-            mConfigs.valueAt(i).writeXml(out);
+            mConfigs.valueAt(i).writeXml(out, version);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f4360e4..576d228 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23,7 +23,6 @@
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
 import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
 import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -98,7 +97,6 @@
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
@@ -214,6 +212,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.PatternMatcher;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -238,7 +237,6 @@
 import android.security.KeyStore;
 import android.security.SystemKeyStore;
 import android.service.pm.PackageServiceDumpProto;
-import android.service.textclassifier.TextClassifierService;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -407,7 +405,7 @@
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
     private static final boolean DEBUG_BACKUP = false;
     public static final boolean DEBUG_INSTALL = false;
-    public static final boolean DEBUG_REMOVE = false;
+    public static final boolean DEBUG_REMOVE = true;
     private static final boolean DEBUG_BROADCASTS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
     private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -8692,6 +8690,7 @@
                             disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
                             null /* originalPkgSetting */, null, parseFlags, scanFlags,
                             (pkg == mPlatformPackage), user);
+                    applyPolicy(pkg, parseFlags, scanFlags);
                     scanPackageOnlyLI(request, mFactoryTest, -1L);
                 }
             }
@@ -9981,6 +9980,10 @@
         return scanFlags;
     }
 
+    // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting
+    // the results / removing app data needs to be moved up a level to the callers of this
+    // method. Also, we need to solve the problem of potentially creating a new shared user
+    // setting. That can probably be done later and patch things up after the fact.
     @GuardedBy("mInstallLock")
     private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@@ -10350,7 +10353,7 @@
 
         if (Build.IS_DEBUGGABLE &&
                 pkg.isPrivileged() &&
-                !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+                !SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, true)) {
             PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
         }
 
@@ -13959,29 +13962,45 @@
 
     @Override
     public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String callingPackage,
             int userId) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+        try {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null);
+        } catch (SecurityException e) {
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_USERS,
+                    "Callers need to have either " + Manifest.permission.SUSPEND_APPS + " or "
+                            + Manifest.permission.MANAGE_USERS);
+        }
         final int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "setPackagesSuspended for user " + userId);
+        if (callingUid != Process.ROOT_UID &&
+                !UserHandle.isSameApp(getPackageUid(callingPackage, 0, userId), callingUid)) {
+            throw new IllegalArgumentException("callingPackage " + callingPackage + " does not"
+                    + " belong to calling app id " + UserHandle.getAppId(callingUid));
+        }
 
         if (ArrayUtils.isEmpty(packageNames)) {
             return packageNames;
         }
 
         // List of package names for whom the suspended state has changed.
-        List<String> changedPackages = new ArrayList<>(packageNames.length);
+        final List<String> changedPackages = new ArrayList<>(packageNames.length);
         // List of package names for whom the suspended state is not set as requested in this
         // method.
-        List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        long callingId = Binder.clearCallingIdentity();
+        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+        final long callingId = Binder.clearCallingIdentity();
         try {
-            for (int i = 0; i < packageNames.length; i++) {
-                String packageName = packageNames[i];
-                boolean changed = false;
-                final int appId;
-                synchronized (mPackages) {
+            synchronized (mPackages) {
+                for (int i = 0; i < packageNames.length; i++) {
+                    final String packageName = packageNames[i];
+                    if (packageName == callingPackage) {
+                        Slog.w(TAG, "Calling package: " + callingPackage + "trying to "
+                                + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                        unactionedPackages.add(packageName);
+                        continue;
+                    }
                     final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
                     if (pkgSetting == null
                             || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
@@ -13990,42 +14009,75 @@
                         unactionedPackages.add(packageName);
                         continue;
                     }
-                    appId = pkgSetting.appId;
                     if (pkgSetting.getSuspended(userId) != suspended) {
                         if (!canSuspendPackageForUserLocked(packageName, userId)) {
                             unactionedPackages.add(packageName);
                             continue;
                         }
-                        pkgSetting.setSuspended(suspended, userId);
-                        mSettings.writePackageRestrictionsLPr(userId);
-                        changed = true;
+                        pkgSetting.setSuspended(suspended, callingPackage, appExtras,
+                                launcherExtras, userId);
                         changedPackages.add(packageName);
                     }
                 }
-
-                if (changed && suspended) {
-                    killApplication(packageName, UserHandle.getUid(userId, appId),
-                            "suspending package");
-                }
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
-
+        // TODO (b/75036698): Also send each package a broadcast when suspended state changed
         if (!changedPackages.isEmpty()) {
             sendPackagesSuspendedForUser(changedPackages.toArray(
                     new String[changedPackages.size()]), userId, suspended);
+            synchronized (mPackages) {
+                scheduleWritePackageRestrictionsLocked(userId);
+            }
         }
 
         return unactionedPackages.toArray(new String[unactionedPackages.size()]);
     }
 
     @Override
+    public PersistableBundle getPackageSuspendedAppExtras(String packageName, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        if (getPackageUid(packageName, 0, userId) != callingUid) {
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+        }
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+                throw new IllegalArgumentException("Unknown target package: " + packageName);
+            }
+            final PackageUserState packageUserState = ps.readUserState(userId);
+            return packageUserState.suspended ? packageUserState.suspendedAppExtras : null;
+        }
+    }
+
+    @Override
+    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras,
+            int userId) {
+        final int callingUid = Binder.getCallingUid();
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+                throw new IllegalArgumentException("Unknown target package: " + packageName);
+            }
+            final PackageUserState packageUserState = ps.readUserState(userId);
+            if (packageUserState.suspended) {
+                // TODO (b/75036698): Also send this package a broadcast with the new app extras
+                packageUserState.suspendedAppExtras = appExtras;
+            }
+        }
+    }
+
+    @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isPackageSuspendedForUser for user " + userId);
+        if (getPackageUid(packageName, 0, userId) != callingUid) {
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+        }
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
@@ -14035,6 +14087,21 @@
         }
     }
 
+    void onSuspendingPackageRemoved(String packageName, int userId) {
+        final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+                : new int[] {userId};
+        synchronized (mPackages) {
+            for (PackageSetting ps : mSettings.mPackages.values()) {
+                for (int user : userIds) {
+                    final PackageUserState pus = ps.readUserState(user);
+                    if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
+                        ps.setSuspended(false, null, null, null, user);
+                    }
+                }
+            }
+        }
+    }
+
     @GuardedBy("mPackages")
     private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
         if (isPackageDeviceAdmin(packageName, userId)) {
@@ -14091,6 +14158,11 @@
             return false;
         }
 
+        if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+            Slog.w(TAG, "Cannot suspend package: " + packageName);
+            return false;
+        }
+
         return true;
     }
 
@@ -18607,6 +18679,7 @@
                 }
                 final int removedUserId = (user != null) ? user.getIdentifier()
                         : UserHandle.USER_ALL;
+
                 if (!clearPackageStateForUserLIF(ps, removedUserId, outInfo)) {
                     return false;
                 }
@@ -18615,6 +18688,11 @@
                 return true;
             }
         }
+        if (ps.getPermissionsState().hasPermission(
+                Manifest.permission.SUSPEND_APPS, user.getIdentifier())) {
+            onSuspendingPackageRemoved(packageName, user.getIdentifier());
+        }
+
 
         if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
                 && user.getIdentifier() != UserHandle.USER_ALL)) {
@@ -18753,6 +18831,9 @@
                     true /*notLaunched*/,
                     false /*hidden*/,
                     false /*suspended*/,
+                    null, /*suspendingPackage*/
+                    null, /*suspendedAppExtras*/
+                    null, /*suspendedLauncherExtras*/
                     false /*instantApp*/,
                     false /*virtualPreload*/,
                     null /*lastDisableAppCaller*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d2ef67b..28e32a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -57,12 +57,14 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IUserManager;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -1503,27 +1505,55 @@
     private int runSuspend(boolean suspendedState) {
         final PrintWriter pw = getOutPrintWriter();
         int userId = UserHandle.USER_SYSTEM;
+        final PersistableBundle appExtras = new PersistableBundle();
+        final PersistableBundle launcherExtras = new PersistableBundle();
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
                 case "--user":
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
+                case "--ael":
+                case "--aes":
+                case "--aed":
+                case "--lel":
+                case "--les":
+                case "--led":
+                    final String key = getNextArgRequired();
+                    final String val = getNextArgRequired();
+                    if (!suspendedState) {
+                        break;
+                    }
+                    final PersistableBundle bundleToInsert =
+                            opt.startsWith("--a") ? appExtras : launcherExtras;
+                    switch (opt.charAt(4)) {
+                        case 'l':
+                            bundleToInsert.putLong(key, Long.valueOf(val));
+                            break;
+                        case 'd':
+                            bundleToInsert.putDouble(key, Double.valueOf(val));
+                            break;
+                        case 's':
+                            bundleToInsert.putString(key, val);
+                            break;
+                    }
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
             }
         }
 
-        String packageName = getNextArg();
+        final String packageName = getNextArg();
         if (packageName == null) {
             pw.println("Error: package name not specified");
             return 1;
         }
-
+        final String callingPackage =
+                (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
         try {
             mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
-                    userId);
+                    appExtras, launcherExtras, callingPackage, userId);
             pw.println("Package " + packageName + " new suspended state: "
                     + mInterface.isPackageSuspendedForUser(packageName, userId));
             return 0;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index a0ed126..008a81c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,12 +20,16 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
 import android.service.pm.PackageProto;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -394,8 +398,13 @@
         return readUserState(userId).suspended;
     }
 
-    void setSuspended(boolean suspended, int userId) {
-        modifyUserState(userId).suspended = suspended;
+    void setSuspended(boolean suspended, String suspendingPackage, PersistableBundle appExtras,
+            PersistableBundle launcherExtras, int userId) {
+        final PackageUserState existingUserState = modifyUserState(userId);
+        existingUserState.suspended = suspended;
+        existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
+        existingUserState.suspendedAppExtras = suspended ? appExtras : null;
+        existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
     }
 
     public boolean getInstantApp(int userId) {
@@ -415,7 +424,9 @@
     }
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
+            boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
+            PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras,
+            boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
             ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
             int domainVerifState, int linkGeneration, int installReason,
@@ -428,6 +439,9 @@
         state.notLaunched = notLaunched;
         state.hidden = hidden;
         state.suspended = suspended;
+        state.suspendingPackage = suspendingPackage;
+        state.suspendedAppExtras = suspendedAppExtras;
+        state.suspendedLauncherExtras = suspendedLauncherExtras;
         state.lastDisableAppCaller = lastDisableAppCaller;
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
@@ -594,6 +608,9 @@
             proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
             proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
             proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
+            if (state.suspended) {
+                proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
+            }
             proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
             proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
             proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a38cbda..d0e8544 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -31,6 +31,7 @@
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -58,6 +59,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PatternMatcher;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -199,6 +201,8 @@
     private static final String TAG_DEFAULT_BROWSER = "default-browser";
     private static final String TAG_DEFAULT_DIALER = "default-dialer";
     private static final String TAG_VERSION = "version";
+    private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
+    private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
 
     public static final String ATTR_NAME = "name";
     public static final String ATTR_PACKAGE = "package";
@@ -217,6 +221,7 @@
     // New name for the above attribute.
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_SUSPENDED = "suspended";
+    private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
     // Legacy, uninstall blocks are stored separately.
     @Deprecated
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -728,6 +733,9 @@
                                 true /*notLaunched*/,
                                 false /*hidden*/,
                                 false /*suspended*/,
+                                null, /*suspendingPackage*/
+                                null, /*suspendedAppExtras*/
+                                null, /*suspendedLauncherExtras*/
                                 instantApp,
                                 virtualPreload,
                                 null /*lastDisableAppCaller*/,
@@ -1619,6 +1627,9 @@
                                 false /*notLaunched*/,
                                 false /*hidden*/,
                                 false /*suspended*/,
+                                null, /*suspendingPackage*/
+                                null, /*suspendedAppExtras*/
+                                null, /*suspendedLauncherExtras*/
                                 false /*instantApp*/,
                                 false /*virtualPreload*/,
                                 null /*lastDisableAppCaller*/,
@@ -1691,6 +1702,12 @@
 
                     final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
                             false);
+                    String suspendingPackage = parser.getAttributeValue(null,
+                            ATTR_SUSPENDING_PACKAGE);
+                    if (suspended && suspendingPackage == null) {
+                        suspendingPackage = PLATFORM_PACKAGE_NAME;
+                    }
+
                     final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser,
                             ATTR_BLOCK_UNINSTALL, false);
                     final boolean instantApp = XmlUtils.readBooleanAttribute(parser,
@@ -1716,6 +1733,8 @@
 
                     ArraySet<String> enabledComponents = null;
                     ArraySet<String> disabledComponents = null;
+                    PersistableBundle suspendedAppExtras = null;
+                    PersistableBundle suspendedLauncherExtras = null;
 
                     int packageDepth = parser.getDepth();
                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1725,11 +1744,22 @@
                                 || type == XmlPullParser.TEXT) {
                             continue;
                         }
-                        tagName = parser.getName();
-                        if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
-                            enabledComponents = readComponentsLPr(parser);
-                        } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
-                            disabledComponents = readComponentsLPr(parser);
+                        switch (parser.getName()) {
+                            case TAG_ENABLED_COMPONENTS:
+                                enabledComponents = readComponentsLPr(parser);
+                                break;
+                            case TAG_DISABLED_COMPONENTS:
+                                disabledComponents = readComponentsLPr(parser);
+                                break;
+                            case TAG_SUSPENDED_APP_EXTRAS:
+                                suspendedAppExtras = PersistableBundle.restoreFromXml(parser);
+                                break;
+                            case TAG_SUSPENDED_LAUNCHER_EXTRAS:
+                                suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
+                                break;
+                            default:
+                                Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
+                                        + TAG_PACKAGE);
                         }
                     }
 
@@ -1737,7 +1767,8 @@
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, instantApp, virtualPreload, enabledCaller,
+                            hidden, suspended, suspendingPackage, suspendedAppExtras,
+                            suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
                             enabledComponents, disabledComponents, verifState, linkGeneration,
                             installReason, harmfulAppWarning);
                 } else if (tagName.equals("preferred-activities")) {
@@ -2046,6 +2077,27 @@
                 }
                 if (ustate.suspended) {
                     serializer.attribute(null, ATTR_SUSPENDED, "true");
+                    serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, ustate.suspendingPackage);
+                    if (ustate.suspendedAppExtras != null) {
+                        serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
+                        try {
+                            ustate.suspendedAppExtras.saveToXml(serializer);
+                        } catch (XmlPullParserException xmle) {
+                            Slog.wtf(TAG, "Exception while trying to write suspendedAppExtras for "
+                                    + pkg + ". Will be lost on reboot", xmle);
+                        }
+                        serializer.endTag(null, TAG_SUSPENDED_APP_EXTRAS);
+                    }
+                    if (ustate.suspendedLauncherExtras != null) {
+                        serializer.startTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
+                        try {
+                            ustate.suspendedLauncherExtras.saveToXml(serializer);
+                        } catch (XmlPullParserException xmle) {
+                            Slog.wtf(TAG, "Exception while trying to write suspendedLauncherExtras"
+                                    + " for " + pkg + ". Will be lost on reboot", xmle);
+                        }
+                        serializer.endTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
+                    }
                 }
                 if (ustate.instantApp) {
                     serializer.attribute(null, ATTR_INSTANT_APP, "true");
@@ -4697,6 +4749,10 @@
             pw.print(ps.getHidden(user.id));
             pw.print(" suspended=");
             pw.print(ps.getSuspended(user.id));
+            if (ps.getSuspended(user.id)) {
+                pw.print(" suspendingPackage=");
+                pw.print(ps.readUserState(user.id).suspendingPackage);
+            }
             pw.print(" stopped=");
             pw.print(ps.getStopped(user.id));
             pw.print(" notLaunched=");
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 062a6b8..41ed6f2 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -198,7 +198,14 @@
         ParcelFileDescriptor fd = null;
         try {
             fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
-            postSuccess(packageName, fd, callback);
+            if (fd == null || !fd.getFileDescriptor().valid()) {
+                Slog.wtf(TAG,
+                        "ParcelFileDescriptor.open returned an invalid descriptor for "
+                                + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
+                postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+            } else {
+                postSuccess(packageName, fd, callback);
+            }
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
                     + snapshotProfile, e);
@@ -264,7 +271,7 @@
         mHandler.post(() -> {
             try {
                 callback.onError(errCode);
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
             }
         });
@@ -277,8 +284,17 @@
         }
         mHandler.post(() -> {
             try {
-                callback.onSuccess(fd);
-            } catch (RemoteException e) {
+                // Double check that the descriptor is still valid.
+                // We've seen production issues (b/76028139) where this can turn invalid (there are
+                // suspicions around the finalizer behaviour).
+                if (fd.getFileDescriptor().valid()) {
+                    callback.onSuccess(fd);
+                } else {
+                    Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for "
+                            + packageName);
+                    callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+                }
+            } catch (Exception e) {
                 Slog.w(TAG,
                         "Failed to call onSuccess after profile snapshot for " + packageName, e);
             } finally {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bf85f30..ad32ed3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -55,6 +55,7 @@
 import android.security.Credentials;
 import android.service.textclassifier.TextClassifierService;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -263,13 +264,9 @@
     }
 
     public void grantDefaultPermissions(int userId) {
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
-            grantAllRuntimePermissions(userId);
-        } else {
-            grantPermissionsToSysComponentsAndPrivApps(userId);
-            grantDefaultSystemHandlerPermissions(userId);
-            grantDefaultPermissionExceptions(userId);
-        }
+        grantPermissionsToSysComponentsAndPrivApps(userId);
+        grantDefaultSystemHandlerPermissions(userId);
+        grantDefaultPermissionExceptions(userId);
     }
 
     private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
@@ -829,13 +826,11 @@
         }
 
         // TextClassifier Service
-        ComponentName textClassifierComponent =
-                TextClassifierService.getServiceComponentName(mContext);
-        if (textClassifierComponent != null) {
-            Intent textClassifierServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
-                    .setComponent(textClassifierComponent);
+        String textClassifierPackageName =
+                mContext.getPackageManager().getSystemTextClassifierPackageName();
+        if (!TextUtils.isEmpty(textClassifierPackageName)) {
             PackageParser.Package textClassifierPackage =
-                    getDefaultSystemHandlerServicePackage(textClassifierServiceIntent, userId);
+                    getSystemPackage(textClassifierPackageName);
             if (textClassifierPackage != null
                     && doesPackageSupportRuntimePermissions(textClassifierPackage)) {
                 grantRuntimePermissions(textClassifierPackage, PHONE_PERMISSIONS, true, userId);
@@ -1248,6 +1243,13 @@
         if (dir.isDirectory() && dir.canRead()) {
             Collections.addAll(ret, dir.listFiles());
         }
+        // For IoT devices, we check the oem partition for default permissions for each app.
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+            dir = new File(Environment.getOemDirectory(), "etc/default-permissions");
+            if (dir.isDirectory() && dir.canRead()) {
+                Collections.addAll(ret, dir.listFiles());
+            }
+        }
         return ret.isEmpty() ? null : ret.toArray(new File[0]);
     }
 
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index c906705..eca6f9f 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -16,10 +16,11 @@
 
 package com.android.server.policy;
 
-import static com.android.server.wm.proto.BarControllerProto.STATE;
-import static com.android.server.wm.proto.BarControllerProto.TRANSIENT_STATE;
+import static com.android.server.wm.BarControllerProto.STATE;
+import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
 
 import android.app.StatusBarManager;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
@@ -68,6 +69,7 @@
     private boolean mShowTransparent;
     private boolean mSetUnHideFlagWhenNextTransparent;
     private boolean mNoAnimationOnNextShow;
+    private final Rect mContentFrame = new Rect();
 
     private OnBarVisibilityChangedListener mVisibilityChangeListener;
 
@@ -87,6 +89,15 @@
         mWin = win;
     }
 
+    /**
+     * Sets the frame within which the bar will display its content.
+     *
+     * This is used to determine if letterboxes interfere with the display of such content.
+     */
+    public void setContentFrame(Rect frame) {
+        mContentFrame.set(frame);
+    }
+
     public void setShowTransparent(boolean transparent) {
         if (transparent != mShowTransparent) {
             mShowTransparent = transparent;
@@ -135,7 +146,8 @@
                 } else {
                     vis &= ~mTranslucentFlag;
                 }
-                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+                        && isTransparentAllowed(win)) {
                     vis |= mTransparentFlag;
                 } else {
                     vis &= ~mTransparentFlag;
@@ -148,6 +160,10 @@
         return vis;
     }
 
+    boolean isTransparentAllowed(WindowState win) {
+        return win == null || !win.isLetterboxedOverlappingWith(mContentFrame);
+    }
+
     public boolean setBarShowingLw(final boolean show) {
         if (mWin == null) return false;
         if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
@@ -328,6 +344,7 @@
             pw.println(StatusBarManager.windowStateToString(mState));
             pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
             pw.println(transientBarStateToString(mTransientBarState));
+            pw.print(prefix); pw.print("  mContentFrame="); pw.println(mContentFrame);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3cd79e1..4dc68ac 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -39,6 +39,7 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
+import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.STATE_OFF;
 import static android.view.WindowManager.DOCKED_LEFT;
@@ -71,6 +72,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -128,26 +130,26 @@
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.FOCUSED_APP_TOKEN;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.FOCUSED_WINDOW;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.FORCE_STATUS_BAR;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.NAVIGATION_BAR;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.ORIENTATION;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.ORIENTATION_LISTENER;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.ROTATION;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.ROTATION_MODE;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.SCREEN_ON_FULLY;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.STATUS_BAR;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
+import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_APP_TOKEN;
+import static com.android.server.wm.WindowManagerPolicyProto.FOCUSED_WINDOW;
+import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR;
+import static com.android.server.wm.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD;
+import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
+import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
+import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
+import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
+import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
+import static com.android.server.wm.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS;
+import static com.android.server.wm.WindowManagerPolicyProto.NAVIGATION_BAR;
+import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION;
+import static com.android.server.wm.WindowManagerPolicyProto.ORIENTATION_LISTENER;
+import static com.android.server.wm.WindowManagerPolicyProto.ROTATION;
+import static com.android.server.wm.WindowManagerPolicyProto.ROTATION_MODE;
+import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
+import static com.android.server.wm.WindowManagerPolicyProto.STATUS_BAR;
+import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW;
+import static com.android.server.wm.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW;
+import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -187,6 +189,7 @@
 import android.hardware.power.V1_0.PowerHint;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.session.MediaSessionLegacyHelper;
@@ -455,6 +458,7 @@
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
     StatusBarManagerInternal mStatusBarManagerInternal;
+    AudioManagerInternal mAudioManagerInternal;
     boolean mPreloadedRecentApps;
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -660,6 +664,7 @@
     static final Rect mTmpStableFrame = new Rect();
     static final Rect mTmpNavigationFrame = new Rect();
     static final Rect mTmpOutsetFrame = new Rect();
+    private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
     private static final Rect mTmpRect = new Rect();
 
     WindowState mTopFullscreenOpaqueWindowState;
@@ -771,6 +776,9 @@
     private boolean mScreenshotChordPowerKeyTriggered;
     private long mScreenshotChordPowerKeyTime;
 
+    // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
+    private int mRingerToggleChord = VOLUME_HUSH_OFF;
+
     private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000;
 
     private boolean mBugreportTvKey1Pressed;
@@ -835,6 +843,7 @@
     private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27;
     private static final int MSG_POWER_VERY_LONG_PRESS = 28;
     private static final int MSG_NOTIFY_USER_ACTIVITY = 29;
+    private static final int MSG_RINGER_TOGGLE_CHORD = 30;
 
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -942,6 +951,8 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                             android.Manifest.permission.USER_ACTIVITY);
+                case MSG_RINGER_TOGGLE_CHORD:
+                    handleRingerChordGesture();
                     break;
             }
         }
@@ -995,10 +1006,13 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
+                    UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POLICY_CONTROL), false, this,
                     UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
                     UserHandle.USER_ALL);
             updateSettings();
@@ -1116,6 +1130,14 @@
     @VisibleForTesting
     SystemGesturesPointerEventListener mSystemGestures;
 
+    private void handleRingerChordGesture() {
+        if (mRingerToggleChord == VOLUME_HUSH_OFF) {
+            return;
+        }
+        getAudioManagerInternal();
+        mAudioManagerInternal.silenceRingerModeInternal("volume_hush");
+    }
+
     IStatusBarService getStatusBarService() {
         synchronized (mServiceAquireLock) {
             if (mStatusBarService == null) {
@@ -1136,6 +1158,15 @@
         }
     }
 
+    AudioManagerInternal getAudioManagerInternal() {
+        synchronized (mServiceAquireLock) {
+            if (mAudioManagerInternal == null) {
+                mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+            }
+            return mAudioManagerInternal;
+        }
+    }
+
     /*
      * We always let the sensor be switched on by default except when
      * the user has explicitly disabled sensor based rotation or when the
@@ -1305,6 +1336,7 @@
             mScreenshotChordPowerKeyTriggered = true;
             mScreenshotChordPowerKeyTime = event.getDownTime();
             interceptScreenshotChord();
+            interceptRingerToggleChord();
         }
 
         // Stop ringing or end call if configured to do so when power is pressed.
@@ -1700,6 +1732,22 @@
         }
     }
 
+    private void interceptRingerToggleChord() {
+        if (mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF
+                && mScreenshotChordPowerKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered) {
+            final long now = SystemClock.uptimeMillis();
+            if (now <= mA11yShortcutChordVolumeUpKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
+                    && now <= mScreenshotChordPowerKeyTime
+                    + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
+                mA11yShortcutChordVolumeUpKeyConsumed = true;
+                cancelPendingPowerKeyAction();
+
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
+                        getRingerToggleChordDelay());
+            }
+        }
+    }
+
     private long getAccessibilityShortcutTimeout() {
         ViewConfiguration config = ViewConfiguration.get(mContext);
         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -1717,6 +1765,11 @@
         return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
     }
 
+    private long getRingerToggleChordDelay() {
+        // Always timeout like a tap
+        return ViewConfiguration.getTapTimeout();
+    }
+
     private void cancelPendingScreenshotChordAction() {
         mHandler.removeCallbacks(mScreenshotRunnable);
     }
@@ -1725,6 +1778,10 @@
         mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
     }
 
+    private void cancelPendingRingerToggleChordAction() {
+        mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
+    }
+
     private final Runnable mEndCallLongPress = new Runnable() {
         @Override
         public void run() {
@@ -2381,7 +2438,13 @@
             mSystemNavigationKeysEnabled = Settings.Secure.getIntForUser(resolver,
                     Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
                     0, UserHandle.USER_CURRENT) == 1;
-
+            mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
+                    UserHandle.USER_CURRENT);
+            if (!mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
+                mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
+            }
             // Configure rotation suggestions.
             int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
                     Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
@@ -2521,7 +2584,14 @@
     /** {@inheritDoc} */
     @Override
     public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
-        int type = attrs.type;
+        final int type = attrs.type;
+        final boolean isRoundedCornerOverlay =
+                (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+
+        if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
+                != PERMISSION_GRANTED) {
+            return ADD_PERMISSION_DENIED;
+        }
 
         outAppOp[0] = AppOpsManager.OP_NONE;
 
@@ -3530,6 +3600,25 @@
             }
         }
 
+        // If a ringer toggle chord could be on the way but we're not sure, then tell the dispatcher
+        // to wait a little while and try again later before dispatching.
+        if (mRingerToggleChord != VOLUME_HUSH_OFF && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
+            if (mA11yShortcutChordVolumeUpKeyTriggered && !mScreenshotChordPowerKeyTriggered) {
+                final long now = SystemClock.uptimeMillis();
+                final long timeoutTime = mA11yShortcutChordVolumeUpKeyTime
+                        + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
+                if (now < timeoutTime) {
+                    return timeoutTime - now;
+                }
+            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
+                if (!down) {
+                    mA11yShortcutChordVolumeUpKeyConsumed = false;
+                }
+                return -1;
+            }
+        }
+
         // Cancel any pending meta actions if we see any other keys being pressed between the down
         // of the meta key and its corresponding up.
         if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
@@ -4636,7 +4725,8 @@
 
             w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
                     df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
-                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout);
+                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout,
+                    false /* parentFrameWasClippedByDisplayCutout */);
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4699,12 +4789,19 @@
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                 vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
                 dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
-                displayFrames.mDisplayCutout);
+                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top
                 + mStatusBarHeightForRotation[displayFrames.mRotation];
 
+        // Tell the bar controller where the collapsed status bar content is
+        mTmpRect.set(mStatusBar.getContentFrameLw());
+        mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+        mTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
+        mTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
+        mStatusBarController.setContentFrame(mTmpRect);
+
         boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
         boolean statusBarTranslucent = (sysui
                 & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
@@ -4837,7 +4934,9 @@
         mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                 mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
                 mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
-                displayFrames.mDisplayCutout);
+                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
+
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
@@ -5290,10 +5389,10 @@
                         vf.set(cf);
                     }
                 }
-                pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
             }
         }
 
+        boolean parentFrameWasClippedByDisplayCutout = false;
         final int cutoutMode = attrs.layoutInDisplayCutoutMode;
         final boolean attachedInParent = attached != null && !layoutInScreen;
         final boolean requestedHideNavigation =
@@ -5301,7 +5400,7 @@
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
-            final Rect displayCutoutSafeExceptMaybeBars = mTmpRect;
+            final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
             displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
             if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
                     && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5330,10 +5429,12 @@
                 // The IME can always extend under the bottom cutout if the navbar is there.
                 displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
             }
-            // Windows that are attached to a parent and laid out in said parent are already
-            // avoidingthe cutout according to that parent and don't need to be further constrained.
+            // Windows that are attached to a parent and laid out in said parent already avoid
+            // the cutout according to that parent and don't need to be further constrained.
             if (!attachedInParent) {
+                mTmpRect.set(pf);
                 pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+                parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
             }
             // Make sure that NO_LIMITS windows clipped to the display don't extend under the
             // cutout.
@@ -5391,7 +5492,8 @@
                 + " sf=" + sf.toShortString()
                 + " osf=" + (osf == null ? "null" : osf.toShortString()));
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
+                parentFrameWasClippedByDisplayCutout);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
@@ -5983,6 +6085,9 @@
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                     if (down) {
+                        // Any activity on the vol down button stops the ringer toggle shortcut
+                        cancelPendingRingerToggleChordAction();
+
                         if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                                 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                             mScreenshotChordVolumeDownKeyTriggered = true;
@@ -6006,12 +6111,16 @@
                             mA11yShortcutChordVolumeUpKeyConsumed = false;
                             cancelPendingPowerKeyAction();
                             cancelPendingScreenshotChordAction();
+                            cancelPendingRingerToggleChordAction();
+
                             interceptAccessibilityShortcutChord();
+                            interceptRingerToggleChord();
                         }
                     } else {
                         mA11yShortcutChordVolumeUpKeyTriggered = false;
                         cancelPendingScreenshotChordAction();
                         cancelPendingAccessibilityShortcutAction();
+                        cancelPendingRingerToggleChordAction();
                     }
                 }
                 if (down) {
@@ -8025,11 +8134,8 @@
             // If the top fullscreen-or-dimming window is also the top fullscreen, respect
             // its light flag.
             vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            if (!statusColorWin.isLetterboxedForDisplayCutoutLw()) {
-                // Only allow white status bar if the window was not letterboxed.
-                vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
-                        & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            }
+            vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
+                    & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
         } else if (statusColorWin != null && statusColorWin.isDimming()) {
             // Otherwise if it's dimming, clear the light flag.
             vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
@@ -8097,15 +8203,6 @@
         return vis;
     }
 
-    private boolean drawsSystemBarBackground(WindowState win) {
-        return win == null || (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-    }
-
-    private boolean forcesDrawStatusBarBackground(WindowState win) {
-        return win == null || (win.getAttrs().privateFlags
-                & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
-    }
-
     private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
         final boolean dockedStackVisible =
                 mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -8129,13 +8226,9 @@
                 mTopDockedOpaqueWindowState, 0, 0);
 
         final boolean fullscreenDrawsStatusBarBackground =
-                (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
-                        && (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
-                || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
+                drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
         final boolean dockedDrawsStatusBarBackground =
-                (drawsSystemBarBackground(mTopDockedOpaqueWindowState)
-                        && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
-                || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);
+                drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
 
         // prevent status bar interaction from clearing certain flags
         int type = win.getAttrs().type;
@@ -8238,6 +8331,22 @@
         return vis;
     }
 
+    private boolean drawsStatusBarBackground(int vis, WindowState win) {
+        if (!mStatusBarController.isTransparentAllowed(win)) {
+            return false;
+        }
+        if (win == null) {
+            return true;
+        }
+
+        final boolean drawsSystemBars =
+                (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+        final boolean forceDrawsSystemBars =
+                (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+
+        return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
+    }
+
     /**
      * @return the current visibility flags with the nav-bar opacity related flags toggled based
      *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b0d5e1a..ec0521d 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -220,10 +220,13 @@
          * @param outsetFrame The frame that includes areas that aren't part of the surface but we
          * want to treat them as such.
          * @param displayCutout the display cutout
+         * @param parentFrameWasClippedByDisplayCutout true if the parent frame would have been
+         * different if there was no display cutout.
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
                 Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
-                Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout);
+                Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
+                boolean parentFrameWasClippedByDisplayCutout);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -447,6 +450,14 @@
             return false;
         }
 
+        /**
+         * Returns true if the window has a letterbox and any part of that letterbox overlaps with
+         * the given {@code rect}.
+         */
+        default boolean isLetterboxedOverlappingWith(Rect rect) {
+            return false;
+        }
+
         /** @return the current windowing mode of this window. */
         int getWindowingMode();
 
@@ -477,7 +488,7 @@
         boolean canAcquireSleepToken();
 
         /**
-         * Writes {@link com.android.server.wm.proto.IdentifierProto} to stream.
+         * Writes {@link com.android.server.wm.IdentifierProto} to stream.
          */
         void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
     }
@@ -1615,7 +1626,7 @@
 
     /**
      * Write the WindowManagerPolicy's state into the protocol buffer.
-     * The message is described in {@link com.android.server.wm.proto.WindowManagerPolicyProto}
+     * The message is described in {@link com.android.server.wm.WindowManagerPolicyProto}
      *
      * @param proto The protocol buffer output stream to write to.
      */
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 48a196d..1508c9e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -16,8 +16,8 @@
 
 package com.android.server.policy;
 
-import static com.android.server.wm.proto.WindowOrientationListenerProto.ENABLED;
-import static com.android.server.wm.proto.WindowOrientationListenerProto.ROTATION;
+import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
+import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
 
 import android.content.Context;
 import android.hardware.Sensor;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 58e8f77..062b154 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -1,11 +1,11 @@
 package com.android.server.policy.keyguard;
 
 import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.wm.proto.KeyguardServiceDelegateProto.INTERACTIVE_STATE;
-import static com.android.server.wm.proto.KeyguardServiceDelegateProto.OCCLUDED;
-import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SCREEN_STATE;
-import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SECURE;
-import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SHOWING;
+import static com.android.server.wm.KeyguardServiceDelegateProto.INTERACTIVE_STATE;
+import static com.android.server.wm.KeyguardServiceDelegateProto.OCCLUDED;
+import static com.android.server.wm.KeyguardServiceDelegateProto.SCREEN_STATE;
+import static com.android.server.wm.KeyguardServiceDelegateProto.SECURE;
+import static com.android.server.wm.KeyguardServiceDelegateProto.SHOWING;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 055e6ea..dd88cd1 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -69,7 +69,6 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.KeyValueListParser;
-import android.util.MathUtils;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -408,7 +407,7 @@
     private boolean mDreamsActivateOnDockSetting;
 
     // True if doze should not be started until after the screen off transition.
-    private boolean mDozeAfterScreenOffConfig;
+    private boolean mDozeAfterScreenOff;
 
     // The minimum screen off timeout, in milliseconds.
     private long mMinimumScreenOffTimeoutConfig;
@@ -896,8 +895,8 @@
                 com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenNotPowered);
         mDreamsBatteryLevelDrainCutoffConfig = resources.getInteger(
                 com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff);
-        mDozeAfterScreenOffConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_dozeAfterScreenOff);
+        mDozeAfterScreenOff = resources.getBoolean(
+                com.android.internal.R.bool.config_dozeAfterScreenOffByDefault);
         mMinimumScreenOffTimeoutConfig = resources.getInteger(
                 com.android.internal.R.integer.config_minimumScreenOffTimeout);
         mMaximumScreenDimDurationConfig = resources.getInteger(
@@ -1724,7 +1723,7 @@
 
                 // Update wireless dock detection state.
                 final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
-                        mIsPowered, mPlugType, mBatteryLevel);
+                        mIsPowered, mPlugType);
 
                 // Treat plugging and unplugging the devices as a user activity.
                 // Users find it disconcerting when they plug or unplug the device
@@ -2507,7 +2506,7 @@
             if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
                 return DisplayPowerRequest.POLICY_DOZE;
             }
-            if (mDozeAfterScreenOffConfig) {
+            if (mDozeAfterScreenOff) {
                 return DisplayPowerRequest.POLICY_OFF;
             }
             // Fall through and preserve the current screen policy if not configured to
@@ -3094,6 +3093,12 @@
         light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
     }
 
+    private void setDozeAfterScreenOffInternal(boolean on) {
+        synchronized (mLock) {
+            mDozeAfterScreenOff = on;
+        }
+    }
+
     private void boostScreenBrightnessInternal(long eventTime, int uid) {
         synchronized (mLock) {
             if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
@@ -3372,7 +3377,7 @@
             pw.println("  mDreamsEnabledSetting=" + mDreamsEnabledSetting);
             pw.println("  mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
             pw.println("  mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
-            pw.println("  mDozeAfterScreenOffConfig=" + mDozeAfterScreenOffConfig);
+            pw.println("  mDozeAfterScreenOff=" + mDozeAfterScreenOff);
             pw.println("  mLowPowerModeSetting=" + mLowPowerModeSetting);
             pw.println("  mAutoLowPowerModeConfigured=" + mAutoLowPowerModeConfigured);
             pw.println("  mAutoLowPowerModeSnoozing=" + mAutoLowPowerModeSnoozing);
@@ -3656,7 +3661,7 @@
                     mDreamsActivateOnDockSetting);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto.IS_DOZE_AFTER_SCREEN_OFF_CONFIG,
-                    mDozeAfterScreenOffConfig);
+                    mDozeAfterScreenOff);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto.IS_LOW_POWER_MODE_SETTING,
                     mLowPowerModeSetting);
@@ -4603,6 +4608,19 @@
         }
 
         @Override // Binder call
+        public void setDozeAfterScreenOff(boolean on) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                setDozeAfterScreenOffInternal(on);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         public void boostScreenBrightness(long eventTime) {
             if (eventTime > SystemClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 54487e3..18e5ce4 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -84,10 +84,6 @@
     // The minimum number of samples that must be collected.
     private static final int MIN_SAMPLES = 3;
 
-    // Upper bound on the battery charge percentage in order to consider turning
-    // the screen on when the device starts charging wirelessly.
-    private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
-
     // To detect movement, we compute the angle between the gravity vector
     // at rest and the current gravity vector.  This field specifies the
     // cosine of the maximum angle variance that we tolerate while at rest.
@@ -214,11 +210,10 @@
      *
      * @param isPowered True if the device is powered.
      * @param plugType The current plug type.
-     * @param batteryLevel The current battery level.
      * @return True if the device is determined to have just been docked on a wireless
      * charger, after suppressing spurious docking or undocking signals.
      */
-    public boolean update(boolean isPowered, int plugType, int batteryLevel) {
+    public boolean update(boolean isPowered, int plugType) {
         synchronized (mLock) {
             final boolean wasPoweredWirelessly = mPoweredWirelessly;
 
@@ -249,13 +244,9 @@
             }
 
             // Report that the device has been docked only if the device just started
-            // receiving power wirelessly, has a high enough battery level that we
-            // can be assured that charging was not delayed due to the battery previously
-            // having been full, and the device is not known to already be at rest
+            // receiving power wirelessly and the device is not known to already be at rest
             // on the wireless charger from earlier.
-            return mPoweredWirelessly && !wasPoweredWirelessly
-                    && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT
-                    && !mAtRest;
+            return mPoweredWirelessly && !wasPoweredWirelessly && !mAtRest;
         }
     }
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index a9a1456..c527533 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -16,18 +16,12 @@
 package com.android.server.power.batterysaver;
 
 import android.Manifest;
-import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.Notification;
-import android.app.Notification.BigTextStyle;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.power.V1_0.PowerHint;
-import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -40,10 +34,7 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
@@ -76,8 +67,6 @@
 
     private final BatterySavingStats mBatterySavingStats;
 
-    private static final String WARNING_LINK_URL = "http://goto.google.com/extreme-battery-saver";
-
     @GuardedBy("mLock")
     private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>();
 
@@ -331,12 +320,6 @@
         }
 
         if (sendBroadcast) {
-            if (enabled) {
-                // STOPSHIP Remove the toast.
-                postWarningNotification();
-            } else {
-                cancelWarningNotification();
-            }
 
             if (DEBUG) {
                 Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
@@ -368,53 +351,6 @@
         }
     }
 
-    private void postWarningNotification() {
-        final UserHandle foregroundUser = UserHandle.of(ActivityManager.getCurrentUser());
-
-        final PendingIntent pendingIntent = PendingIntent
-                .getActivityAsUser(mContext, 0,
-                        new Intent(Intent.ACTION_VIEW, Uri.parse(WARNING_LINK_URL)),
-                        PendingIntent.FLAG_CANCEL_CURRENT, null,
-                       foregroundUser);
-
-        final CharSequence title = mContext.getString
-                (com.android.internal.R.string.battery_saver_warning_title);
-        final CharSequence text = mContext.getString
-                (com.android.internal.R.string.battery_saver_warning);
-
-        final Notification notification =
-                new Notification.Builder(mContext, SystemNotificationChannels.ALERTS)
-                .setSmallIcon(R.drawable.stat_notify_error)
-                .setTicker(title)
-                .setWhen(System.currentTimeMillis())
-                .setContentTitle(title)
-                .setContentText(text)
-                .setContentIntent(pendingIntent)
-                .setStyle(new BigTextStyle().bigText(text))
-                .build();
-
-        final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
-
-        if (nm != null) {
-            nm.notifyAsUser(title.toString(),
-                    SystemMessage.NOTE_BATTERY_SAVER_WARNING,
-                    notification,
-                    foregroundUser);
-        }
-    }
-
-    private void cancelWarningNotification() {
-        final UserHandle foregroundUser = UserHandle.of(ActivityManager.getCurrentUser());
-        final CharSequence title = mContext.getString
-                (com.android.internal.R.string.battery_saver_warning_title);
-
-        final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
-        if (nm != null) {
-            nm.cancelAsUser(title.toString(), SystemMessage.NOTE_BATTERY_SAVER_WARNING,
-                    foregroundUser);
-        }
-    }
-
     private void updateBatterySavingStats() {
         final PowerManager pm = getPowerManager();
         if (pm == null) {
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index a7dfd35..0b7d9d0 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.slice;
 
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
 import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.content.ContentProvider.maybeAddUserId;
@@ -31,6 +33,7 @@
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceManager;
 import android.app.slice.SliceSpec;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -95,6 +98,7 @@
     private final AtomicFile mSliceAccessFile;
     @GuardedBy("mAccessList")
     private final SliceFullAccessList mAccessList;
+    private final UsageStatsManagerInternal mAppUsageStats;
 
     public SliceManagerService(Context context) {
         this(context, createHandler().getLooper());
@@ -112,6 +116,7 @@
         final File systemDir = new File(Environment.getDataDirectory(), "system");
         mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
         mAccessList = new SliceFullAccessList(mContext);
+        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
         synchronized (mSliceAccessFile) {
             if (!mSliceAccessFile.exists()) return;
@@ -166,8 +171,19 @@
     public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
-        uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+        int user = Binder.getCallingUserHandle().getIdentifier();
+        uri = maybeAddUserId(uri, user);
         getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token);
+
+        Uri finalUri = uri;
+        mHandler.post(() -> {
+            String slicePkg = getProviderPkg(finalUri, user);
+            if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
+                mAppUsageStats.reportEvent(slicePkg, user,
+                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
+                                ? SLICE_PINNED_PRIV : SLICE_PINNED);
+            }
+        });
     }
 
     @Override
@@ -352,38 +368,45 @@
             if (getContext().checkUriPermission(uri, pid, uid,
                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
                 // Last fallback (if the calling app owns the authority, then it can have access).
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    IBinder token = new Binder();
-                    IActivityManager activityManager = ActivityManager.getService();
-                    ContentProviderHolder holder = null;
-                    String providerName = getUriWithoutUserId(uri).getAuthority();
-                    try {
-                        try {
-                            holder = activityManager.getContentProviderExternal(
-                                    providerName, getUserIdFromUri(uri, user), token);
-                            if (holder == null || holder.info == null
-                                    || !Objects.equals(holder.info.packageName, pkg)) {
-                                return PERMISSION_DENIED;
-                            }
-                        } finally {
-                            if (holder != null && holder.provider != null) {
-                                activityManager.removeContentProviderExternal(providerName, token);
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        // Can't happen.
-                        e.rethrowAsRuntimeException();
-                    }
-                } finally {
-                    // I know, the double finally seems ugly, but seems safest for the identity.
-                    Binder.restoreCallingIdentity(ident);
+                if (!Objects.equals(getProviderPkg(uri, user), pkg)) {
+                    return PERMISSION_DENIED;
                 }
             }
         }
         return PERMISSION_GRANTED;
     }
 
+    private String getProviderPkg(Uri uri, int user) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            IBinder token = new Binder();
+            IActivityManager activityManager = ActivityManager.getService();
+            ContentProviderHolder holder = null;
+            String providerName = getUriWithoutUserId(uri).getAuthority();
+            try {
+                try {
+                    holder = activityManager.getContentProviderExternal(
+                            providerName, getUserIdFromUri(uri, user), token);
+                    if (holder != null && holder.info != null) {
+                        return holder.info.packageName;
+                    } else {
+                        return null;
+                    }
+                } finally {
+                    if (holder != null && holder.provider != null) {
+                        activityManager.removeContentProviderExternal(providerName, token);
+                    }
+                }
+            } catch (RemoteException e) {
+                // Can't happen.
+                throw e.rethrowAsRuntimeException();
+            }
+        } finally {
+            // I know, the double finally seems ugly, but seems safest for the identity.
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private void enforceCrossUser(String pkg, Uri uri) {
         int user = Binder.getCallingUserHandle().getIdentifier();
         if (getUserIdFromUri(uri, user) != user) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index afcedcc..74c5ee9 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -177,7 +177,6 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         mKernelUidCpuFreqTimeReader.setThrottleInterval(0);
         long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile);
-        mKernelUidCpuFreqTimeReader.setReadBinary(true);
         mKernelUidCpuClusterTimeReader.setThrottleInterval(0);
         mKernelUidCpuActiveTimeReader.setThrottleInterval(0);
     }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 6053512..6df7092 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -25,12 +27,14 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.Slog;
-import android.service.textclassifier.ITextClassifierService;
+import android.os.UserHandle;
 import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 import android.service.textclassifier.TextClassifierService;
+import android.util.Slog;
+import android.util.SparseArray;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassifier;
@@ -38,12 +42,13 @@
 import android.view.textclassifier.TextSelection;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
-import java.util.LinkedList;
+import java.util.ArrayDeque;
 import java.util.Queue;
-import java.util.concurrent.Callable;
 
 /**
  * A manager for TextClassifier services.
@@ -73,58 +78,44 @@
                 Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t);
             }
         }
+
+        @Override
+        public void onStartUser(int userId) {
+            processAnyPendingWork(userId);
+        }
+
+        @Override
+        public void onUnlockUser(int userId) {
+            // Rebind if we failed earlier due to locked encrypted user
+            processAnyPendingWork(userId);
+        }
+
+        private void processAnyPendingWork(int userId) {
+            synchronized (mManagerService.mLock) {
+                mManagerService.getUserStateLocked(userId).bindIfHasPendingRequestsLocked();
+            }
+        }
+
+        @Override
+        public void onStopUser(int userId) {
+            synchronized (mManagerService.mLock) {
+                UserState userState = mManagerService.peekUserStateLocked(userId);
+                if (userState != null) {
+                    userState.mConnection.cleanupService();
+                    mManagerService.mUserStates.remove(userId);
+                }
+            }
+        }
+
     }
 
     private final Context mContext;
-    private final Intent mServiceIntent;
-    private final ServiceConnection mConnection;
     private final Object mLock;
     @GuardedBy("mLock")
-    private final Queue<PendingRequest> mPendingRequests;
-
-    @GuardedBy("mLock")
-    private ITextClassifierService mService;
-    @GuardedBy("mLock")
-    private boolean mBinding;
+    final SparseArray<UserState> mUserStates = new SparseArray<>();
 
     private TextClassificationManagerService(Context context) {
         mContext = Preconditions.checkNotNull(context);
-        mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
-                .setComponent(TextClassifierService.getServiceComponentName(mContext));
-        mConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                synchronized (mLock) {
-                    mService = ITextClassifierService.Stub.asInterface(service);
-                    setBindingLocked(false);
-                    handlePendingRequestsLocked();
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                cleanupService();
-            }
-
-            @Override
-            public void onBindingDied(ComponentName name) {
-                cleanupService();
-            }
-
-            @Override
-            public void onNullBinding(ComponentName name) {
-                cleanupService();
-            }
-
-            private void cleanupService() {
-                synchronized (mLock) {
-                    mService = null;
-                    setBindingLocked(false);
-                    handlePendingRequestsLocked();
-                }
-            }
-        };
-        mPendingRequests = new LinkedList<>();
         mLock = new Object();
     }
 
@@ -133,30 +124,20 @@
             CharSequence text, int selectionStartIndex, int selectionEndIndex,
             TextSelection.Options options, ITextSelectionCallback callback)
             throws RemoteException {
-        // TODO(b/72481438): All remote calls need to take userId.
         validateInput(text, selectionStartIndex, selectionEndIndex, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onSuggestSelection(
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onSuggestSelection(
                         text, selectionStartIndex, selectionEndIndex, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onSuggestSelection(
-                            text, selectionStartIndex, selectionEndIndex,
-                            options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onSuggestSelection(
+                                text, selectionStartIndex, selectionEndIndex, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -168,24 +149,16 @@
             throws RemoteException {
         validateInput(text, startIndex, endIndex, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onClassifyText(text, startIndex, endIndex, options, callback);
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onClassifyText(text, startIndex, endIndex, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onClassifyText(text, startIndex, endIndex, options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onClassifyText(text, startIndex, endIndex, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -196,24 +169,16 @@
             throws RemoteException {
         validateInput(text, callback);
 
-        if (!bind()) {
-            callback.onFailure();
-            return;
-        }
-
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onGenerateLinks(text, options, callback);
+            UserState userState = getCallingUserStateLocked();
+            if (!userState.bindLocked()) {
+                callback.onFailure();
+            } else if (userState.isBoundLocked()) {
+                userState.mService.onGenerateLinks(text, options, callback);
             } else {
-                final Callable<Void> request = () -> {
-                    onGenerateLinks(text, options, callback);
-                    return null;
-                };
-                final Callable<Void> onServiceFailure = () -> {
-                    callback.onFailure();
-                    return null;
-                };
-                enqueueRequestLocked(request, onServiceFailure, callback.asBinder());
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onGenerateLinks(text, options, callback),
+                        callback::onFailure, callback.asBinder(), this, userState));
             }
         }
     }
@@ -223,99 +188,63 @@
         validateInput(event, mContext);
 
         synchronized (mLock) {
-            if (isBoundLocked()) {
-                mService.onSelectionEvent(event);
+            UserState userState = getCallingUserStateLocked();
+            if (userState.isBoundLocked()) {
+                userState.mService.onSelectionEvent(event);
             } else {
-                final Callable<Void> request = () -> {
-                    onSelectionEvent(event);
-                    return null;
-                };
-                enqueueRequestLocked(request, null /* onServiceFailure */, null /* binder */);
+                userState.mPendingRequests.add(new PendingRequest(
+                        () -> onSelectionEvent(event),
+                        null /* onServiceFailure */, null /* binder */, this, userState));
             }
         }
     }
 
-    /**
-     * @return true if the service is bound or in the process of being bound.
-     *      Returns false otherwise.
-     */
-    private boolean bind() {
-        synchronized (mLock) {
-            if (isBoundLocked() || isBindingLocked()) {
-                return true;
-            }
+    private UserState getCallingUserStateLocked() {
+        return getUserStateLocked(UserHandle.getCallingUserId());
+    }
 
-            // TODO: Handle bind timeout.
-            final boolean willBind;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                Slog.d(LOG_TAG, "Binding to " + mServiceIntent.getComponent());
-                willBind = mContext.bindServiceAsUser(
-                        mServiceIntent, mConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
-                        Binder.getCallingUserHandle());
-                setBindingLocked(willBind);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            return willBind;
+    private UserState getUserStateLocked(int userId) {
+        UserState result = mUserStates.get(userId);
+        if (result == null) {
+            result = new UserState(userId, mContext, mLock);
+            mUserStates.put(userId, result);
         }
+        return result;
     }
 
-    @GuardedBy("mLock")
-    private boolean isBoundLocked() {
-        return mService != null;
+    UserState peekUserStateLocked(int userId) {
+        return mUserStates.get(userId);
     }
 
-    @GuardedBy("mLock")
-    private boolean isBindingLocked() {
-        return mBinding;
-    }
+    private static final class PendingRequest implements IBinder.DeathRecipient {
 
-    @GuardedBy("mLock")
-    private void setBindingLocked(boolean binding) {
-        mBinding = binding;
-    }
-
-    @GuardedBy("mLock")
-    private void enqueueRequestLocked(
-            Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
-        mPendingRequests.add(new PendingRequest(request, onServiceFailure, binder));
-    }
-
-    @GuardedBy("mLock")
-    private void handlePendingRequestsLocked() {
-        // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService.
-        final PendingRequest[] pendingRequests =
-                mPendingRequests.toArray(new PendingRequest[mPendingRequests.size()]);
-        for (PendingRequest pendingRequest : pendingRequests) {
-            if (isBoundLocked()) {
-                pendingRequest.executeLocked();
-            } else {
-                pendingRequest.notifyServiceFailureLocked();
-            }
-        }
-    }
-
-    private final class PendingRequest implements IBinder.DeathRecipient {
-
-        private final Callable<Void> mRequest;
-        @Nullable private final Callable<Void> mOnServiceFailure;
         @Nullable private final IBinder mBinder;
+        @NonNull private final Runnable mRequest;
+        @Nullable private final Runnable mOnServiceFailure;
+        @GuardedBy("mLock")
+        @NonNull private final UserState mOwningUser;
+        @NonNull private final TextClassificationManagerService mService;
 
         /**
          * Initializes a new pending request.
-         *
          * @param request action to perform when the service is bound
          * @param onServiceFailure action to perform when the service dies or disconnects
          * @param binder binder to the process that made this pending request
+         * @param service
+         * @param owningUser
          */
         PendingRequest(
-                Callable<Void> request, @Nullable Callable<Void> onServiceFailure,
-                @Nullable IBinder binder) {
-            mRequest = Preconditions.checkNotNull(request);
-            mOnServiceFailure = onServiceFailure;
+                @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
+                @Nullable IBinder binder,
+                TextClassificationManagerService service,
+                UserState owningUser) {
+            mRequest =
+                    logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
+            mOnServiceFailure =
+                    logOnFailure(onServiceFailure, "notifying callback of service failure");
             mBinder = binder;
+            mService = service;
+            mOwningUser = owningUser;
             if (mBinder != null) {
                 try {
                     mBinder.linkToDeath(this, 0);
@@ -325,32 +254,9 @@
             }
         }
 
-        @GuardedBy("mLock")
-        void executeLocked() {
-            removeLocked();
-            try {
-                mRequest.call();
-            } catch (Exception e) {
-                Slog.d(LOG_TAG, "Error handling pending request: " + e.getMessage());
-            }
-        }
-
-        @GuardedBy("mLock")
-        void notifyServiceFailureLocked() {
-            removeLocked();
-            if (mOnServiceFailure != null) {
-                try {
-                    mOnServiceFailure.call();
-                } catch (Exception e) {
-                    Slog.d(LOG_TAG, "Error notifying callback of service failure: "
-                            + e.getMessage());
-                }
-            }
-        }
-
         @Override
         public void binderDied() {
-            synchronized (mLock) {
+            synchronized (mService.mLock) {
                 // No need to handle this pending request anymore. Remove.
                 removeLocked();
             }
@@ -358,13 +264,19 @@
 
         @GuardedBy("mLock")
         private void removeLocked() {
-            mPendingRequests.remove(this);
+            mOwningUser.mPendingRequests.remove(this);
             if (mBinder != null) {
                 mBinder.unlinkToDeath(this, 0);
             }
         }
     }
 
+    private static Runnable logOnFailure(@Nullable ThrowingRunnable r, String opDesc) {
+        if (r == null) return null;
+        return FunctionalUtils.handleExceptions(r,
+                e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
+    }
+
     private static void validateInput(
             CharSequence text, int startIndex, int endIndex, Object callback)
             throws RemoteException {
@@ -396,4 +308,119 @@
             throw new RemoteException(e.getMessage());
         }
     }
+
+    private static final class UserState {
+        @UserIdInt final int mUserId;
+        final TextClassifierServiceConnection mConnection = new TextClassifierServiceConnection();
+        @GuardedBy("mLock")
+        final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+        @GuardedBy("mLock")
+        ITextClassifierService mService;
+        @GuardedBy("mLock")
+        boolean mBinding;
+
+        private final Context mContext;
+        private final Object mLock;
+
+        private UserState(int userId, Context context, Object lock) {
+            mUserId = userId;
+            mContext = Preconditions.checkNotNull(context);
+            mLock = Preconditions.checkNotNull(lock);
+        }
+
+        @GuardedBy("mLock")
+        boolean isBoundLocked() {
+            return mService != null;
+        }
+
+        @GuardedBy("mLock")
+        private void handlePendingRequestsLocked() {
+            // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService.
+            PendingRequest request;
+            while ((request = mPendingRequests.poll()) != null) {
+                if (isBoundLocked()) {
+                    request.mRequest.run();
+                } else {
+                    if (request.mOnServiceFailure != null) {
+                        request.mOnServiceFailure.run();
+                    }
+                }
+
+                if (request.mBinder != null) {
+                    request.mBinder.unlinkToDeath(request, 0);
+                }
+            }
+        }
+
+        private boolean bindIfHasPendingRequestsLocked() {
+            return !mPendingRequests.isEmpty() && bindLocked();
+        }
+
+        /**
+         * @return true if the service is bound or in the process of being bound.
+         *      Returns false otherwise.
+         */
+        private boolean bindLocked() {
+            if (isBoundLocked() || mBinding) {
+                return true;
+            }
+
+            // TODO: Handle bind timeout.
+            final boolean willBind;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                ComponentName componentName =
+                        TextClassifierService.getServiceComponentName(mContext);
+                if (componentName == null) {
+                    // Might happen if the storage is encrypted and the user is not unlocked
+                    return false;
+                }
+                Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
+                        .setComponent(componentName);
+                Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
+                willBind = mContext.bindServiceAsUser(
+                        serviceIntent, mConnection,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        UserHandle.of(mUserId));
+                mBinding = willBind;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return willBind;
+        }
+
+        private final class TextClassifierServiceConnection implements ServiceConnection {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                init(ITextClassifierService.Stub.asInterface(service));
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onBindingDied(ComponentName name) {
+                cleanupService();
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                cleanupService();
+            }
+
+            void cleanupService() {
+                init(null);
+            }
+
+            private void init(@Nullable ITextClassifierService service) {
+                synchronized (mLock) {
+                    mService = service;
+                    mBinding = false;
+                    handlePendingRequestsLocked();
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index c31cdec..641a1ba 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1096,10 +1096,7 @@
                     // Add windows of certain types not covered by modal windows.
                     if (isReportedWindowType(windowState.mAttrs.type)) {
                         // Add the window to the ones to be reported.
-                        WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
-                        window.layer = addedWindows.size();
-                        addedWindows.add(window.token);
-                        windows.add(window);
+                        addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
                         if (windowState.isFocused()) {
                             focusedWindowAdded = true;
                         }
@@ -1150,10 +1147,8 @@
                             computeWindowBoundsInScreen(windowState, boundsInScreen);
 
                             // Add the window to the ones to be reported.
-                            WindowInfo window = obtainPopulatedWindowInfo(windowState,
-                                    boundsInScreen);
-                            addedWindows.add(window.token);
-                            windows.add(window);
+                            addPopulatedWindowInfo(
+                                    windowState, boundsInScreen, windows, addedWindows);
                             break;
                         }
                     }
@@ -1244,11 +1239,14 @@
                     (int) windowFrame.right, (int) windowFrame.bottom);
         }
 
-        private static WindowInfo obtainPopulatedWindowInfo(
-                WindowState windowState, Rect boundsInScreen) {
+        private static void addPopulatedWindowInfo(
+                WindowState windowState, Rect boundsInScreen,
+                List<WindowInfo> out, Set<IBinder> tokenOut) {
             final WindowInfo window = windowState.getWindowInfo();
             window.boundsInScreen.set(boundsInScreen);
-            return window;
+            window.layer = tokenOut.size();
+            out.add(window);
+            tokenOut.add(window.token);
         }
 
         private void cacheWindows(List<WindowInfo> windows) {
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index b00e595..9b787de 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -159,6 +159,7 @@
         channel.enableVibration(false);
         channel.setBlockableSystem(true);
         channel.setGroup(sChannelGroup.getId());
+        channel.setBypassDnd(true);
         mNotificationManager.createNotificationChannel(channel);
     }
 
diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
new file mode 100644
index 0000000..ae343da
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+
+/**
+ * Keeps track of all {@link AppWindowToken} that are animating and makes sure all animations are
+ * finished at the same time such that we don't run into issues with z-ordering: An activity A
+ * that has a shorter animation that is above another activity B with a longer animation in the same
+ * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on
+ * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B
+ * until B finishes animating.
+ */
+class AnimatingAppWindowTokenRegistry {
+
+    private ArraySet<AppWindowToken> mAnimatingTokens = new ArraySet<>();
+    private ArrayMap<AppWindowToken, Runnable> mFinishedTokens = new ArrayMap<>();
+
+    private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>();
+
+    /**
+     * Notifies that an {@link AppWindowToken} has started animating.
+     */
+    void notifyStarting(AppWindowToken token) {
+        mAnimatingTokens.add(token);
+    }
+
+    /**
+     * Notifies that an {@link AppWindowToken} has finished animating.
+     */
+    void notifyFinished(AppWindowToken token) {
+        mAnimatingTokens.remove(token);
+        mFinishedTokens.remove(token);
+    }
+
+    /**
+     * Called when an {@link AppWindowToken} is about to finish animating.
+     *
+     * @param endDeferFinishCallback Callback to run when defer finish should be ended.
+     * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise.
+     */
+    boolean notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback) {
+        final boolean removed = mAnimatingTokens.remove(token);
+        if (!removed) {
+            return false;
+        }
+
+        if (mAnimatingTokens.isEmpty()) {
+
+            // If no animations are animating anymore, finish all others.
+            endDeferringFinished();
+            return false;
+        } else {
+
+            // Otherwise let's put it into the pending list of to be finished animations.
+            mFinishedTokens.put(token, endDeferFinishCallback);
+            return true;
+        }
+    }
+
+    private void endDeferringFinished() {
+        // Copy it into a separate temp list to avoid modifying the collection while iterating as
+        // calling the callback may call back into notifyFinished.
+        for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
+            mTmpRunnableList.add(mFinishedTokens.valueAt(i));
+        }
+        mFinishedTokens.clear();
+        for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
+            mTmpRunnableList.get(i).run();
+        }
+        mTmpRunnableList.clear();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 93ca4dc..41ae48a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -70,8 +70,8 @@
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
-import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
-import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
+import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
+import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
 
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 3cd3e8b..ad92f81 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -20,9 +20,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.proto.AppWindowThumbnailProto.HEIGHT;
-import static com.android.server.wm.proto.AppWindowThumbnailProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.proto.AppWindowThumbnailProto.WIDTH;
+import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
 
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
@@ -119,7 +119,7 @@
 
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
-     * com.android.server.wm.proto.AppWindowThumbnailProto}.
+     * com.android.server.wm.AppWindowThumbnailProto}.
      *
      * @param proto Stream to write the AppWindowThumbnail object to.
      * @param fieldId Field Id of the AppWindowThumbnail as defined in the parent message.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 88cb54d..85436da 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -24,7 +24,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.SurfaceControl.HIDDEN;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -52,29 +51,29 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.proto.AppWindowTokenProto.ALL_DRAWN;
-import static com.android.server.wm.proto.AppWindowTokenProto.APP_STOPPED;
-import static com.android.server.wm.proto.AppWindowTokenProto.CLIENT_HIDDEN;
-import static com.android.server.wm.proto.AppWindowTokenProto.DEFER_HIDING_CLIENT;
-import static com.android.server.wm.proto.AppWindowTokenProto.FILLS_PARENT;
-import static com.android.server.wm.proto.AppWindowTokenProto.FROZEN_BOUNDS;
-import static com.android.server.wm.proto.AppWindowTokenProto.HIDDEN_REQUESTED;
-import static com.android.server.wm.proto.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
-import static com.android.server.wm.proto.AppWindowTokenProto.IS_REALLY_ANIMATING;
-import static com.android.server.wm.proto.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
-import static com.android.server.wm.proto.AppWindowTokenProto.LAST_ALL_DRAWN;
-import static com.android.server.wm.proto.AppWindowTokenProto.LAST_SURFACE_SHOWING;
-import static com.android.server.wm.proto.AppWindowTokenProto.NAME;
-import static com.android.server.wm.proto.AppWindowTokenProto.NUM_DRAWN_WINDOWS;
-import static com.android.server.wm.proto.AppWindowTokenProto.NUM_INTERESTING_WINDOWS;
-import static com.android.server.wm.proto.AppWindowTokenProto.REMOVED;
-import static com.android.server.wm.proto.AppWindowTokenProto.REPORTED_DRAWN;
-import static com.android.server.wm.proto.AppWindowTokenProto.REPORTED_VISIBLE;
-import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_DISPLAYED;
-import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_MOVED;
-import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_WINDOW;
-import static com.android.server.wm.proto.AppWindowTokenProto.THUMBNAIL;
-import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
+import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
+import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
+import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
+import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
+import static com.android.server.wm.AppWindowTokenProto.IS_REALLY_ANIMATING;
+import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
+import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.LAST_SURFACE_SHOWING;
+import static com.android.server.wm.AppWindowTokenProto.NAME;
+import static com.android.server.wm.AppWindowTokenProto.NUM_DRAWN_WINDOWS;
+import static com.android.server.wm.AppWindowTokenProto.NUM_INTERESTING_WINDOWS;
+import static com.android.server.wm.AppWindowTokenProto.REMOVED;
+import static com.android.server.wm.AppWindowTokenProto.REPORTED_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.REPORTED_VISIBLE;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_DISPLAYED;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
+import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
+import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
 
 import android.annotation.CallSuper;
 import android.app.Activity;
@@ -252,6 +251,7 @@
     private final Point mTmpPoint = new Point();
     private final Rect mTmpRect = new Rect();
     private RemoteAnimationDefinition mRemoteAnimationDefinition;
+    private AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry;
 
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
@@ -712,7 +712,7 @@
         if (destroyedSomething) {
             final DisplayContent dc = getDisplayContent();
             dc.assignWindowLayers(true /*setLayoutNeeded*/);
-            updateLetterbox(null);
+            updateLetterboxSurface(null);
         }
     }
 
@@ -781,6 +781,16 @@
                 task.mStack.mExitingAppTokens.remove(this);
             }
         }
+        final TaskStack stack = getStack();
+
+        // If we reparent, make sure to remove ourselves from the old animation registry.
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
+        }
+        mAnimatingAppWindowTokenRegistry = stack != null
+                ? stack.getAnimatingAppWindowTokenRegistry()
+                : null;
+
         mLastParent = task;
     }
 
@@ -979,7 +989,7 @@
     void removeChild(WindowState child) {
         super.removeChild(child);
         checkKeyguardFlagsChanged();
-        updateLetterbox(child);
+        updateLetterboxSurface(child);
     }
 
     private boolean waitingForReplacement() {
@@ -1470,30 +1480,33 @@
         return isInterestingAndDrawn;
     }
 
-    void updateLetterbox(WindowState winHint) {
+    void layoutLetterbox(WindowState winHint) {
         final WindowState w = findMainWindow();
-        if (w != winHint && winHint != null && w != null) {
+        if (w == null || winHint != null && w != winHint) {
             return;
         }
-        final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow()
-                && fillsParent() && w.hasDrawnLw();
+        final boolean surfaceReady = w.hasDrawnLw()  // Regular case
+                || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
+                || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
+        final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
         if (needsLetterbox) {
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> makeChildSurface(null));
             }
-            mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
+            mLetterbox.layout(getParent().getBounds(), w.mFrame);
         } else if (mLetterbox != null) {
-            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-            // Make sure we have a transaction here, in case we're called outside of a transaction.
-            // This does not use mPendingTransaction, because SurfaceAnimator uses a
-            // global transaction in onAnimationEnd.
-            SurfaceControl.openTransaction();
-            try {
-                mLetterbox.hide(t);
-            } finally {
-                SurfaceControl.mergeToGlobalTransaction(t);
-                SurfaceControl.closeTransaction();
-            }
+            mLetterbox.hide();
+        }
+    }
+
+    void updateLetterboxSurface(WindowState winHint) {
+        final WindowState w = findMainWindow();
+        if (w != winHint && winHint != null && w != null) {
+            return;
+        }
+        layoutLetterbox(winHint);
+        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+            mLetterbox.applySurfaceChanges(mPendingTransaction);
         }
     }
 
@@ -1685,7 +1698,10 @@
                 stack.getBounds(mTmpRect);
                 mTmpRect.offsetTo(0, 0);
             }
-            if (mService.mAppTransition.getRemoteAnimationController() != null) {
+
+            // Delaying animation start isn't compatible with remote animations at all.
+            if (mService.mAppTransition.getRemoteAnimationController() != null
+                    && !mSurfaceAnimator.isAnimationStartDelayed()) {
                 adapter = mService.mAppTransition.getRemoteAnimationController()
                         .createAnimationAdapter(this, mTmpPoint, mTmpRect);
             } else {
@@ -1784,6 +1800,21 @@
     }
 
     @Override
+    public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+        return mAnimatingAppWindowTokenRegistry != null
+                && mAnimatingAppWindowTokenRegistry.notifyAboutToFinish(
+                        this, endDeferFinishCallback);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        super.onAnimationLeashDestroyed(t);
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
+        }
+    }
+
+    @Override
     protected void setLayer(Transaction t, int layer) {
         if (!mSurfaceAnimator.hasLeash()) {
             t.setLayer(mSurfaceControl, layer);
@@ -1825,6 +1856,9 @@
 
         final DisplayContent dc = getDisplayContent();
         dc.assignStackOrdering(t);
+        if (mAnimatingAppWindowTokenRegistry != null) {
+            mAnimatingAppWindowTokenRegistry.notifyStarting(this);
+        }
     }
 
     /**
@@ -2163,4 +2197,12 @@
             return new Rect();
         }
     }
+
+    /**
+     * @eturn true if there is a letterbox and any part of that letterbox overlaps with
+     * the given {@code rect}.
+     */
+    boolean isLetterboxOverlappingWith(Rect rect) {
+        return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 2c2389b..627c629 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -29,9 +29,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
-import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION;
-import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
-import static com.android.server.wm.proto.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
+import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
+import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
+import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
 
 import android.annotation.CallSuper;
 import android.app.WindowConfiguration;
@@ -470,7 +470,7 @@
 
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
-     * {@link com.android.server.wm.proto.ConfigurationContainerProto}.
+     * {@link com.android.server.wm.ConfigurationContainerProto}.
      *
      * @param proto    Stream to write the ConfigurationContainer object to.
      * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 5c62987..d000bb6 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -16,10 +16,10 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.proto.AlphaAnimationSpecProto.DURATION;
-import static com.android.server.wm.proto.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.proto.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.proto.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.AlphaAnimationSpecProto.DURATION;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
 
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 59babcf..3e47ea6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -102,19 +101,19 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
-import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
-import static com.android.server.wm.proto.DisplayProto.DISPLAY_FRAMES;
-import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
-import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
-import static com.android.server.wm.proto.DisplayProto.DPI;
-import static com.android.server.wm.proto.DisplayProto.ID;
-import static com.android.server.wm.proto.DisplayProto.IME_WINDOWS;
-import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER;
-import static com.android.server.wm.proto.DisplayProto.ROTATION;
-import static com.android.server.wm.proto.DisplayProto.SCREEN_ROTATION_ANIMATION;
-import static com.android.server.wm.proto.DisplayProto.STACKS;
-import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER;
+import static com.android.server.wm.DisplayProto.ABOVE_APP_WINDOWS;
+import static com.android.server.wm.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.DisplayProto.DISPLAY_FRAMES;
+import static com.android.server.wm.DisplayProto.DISPLAY_INFO;
+import static com.android.server.wm.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
+import static com.android.server.wm.DisplayProto.DPI;
+import static com.android.server.wm.DisplayProto.ID;
+import static com.android.server.wm.DisplayProto.IME_WINDOWS;
+import static com.android.server.wm.DisplayProto.PINNED_STACK_CONTROLLER;
+import static com.android.server.wm.DisplayProto.ROTATION;
+import static com.android.server.wm.DisplayProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayProto.STACKS;
+import static com.android.server.wm.DisplayProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -306,6 +305,11 @@
     int pendingLayoutChanges;
     // TODO(multi-display): remove some of the usages.
     boolean isDefaultDisplay;
+    /**
+     * Flag indicating whether WindowManager should override info for this display in
+     * DisplayManager.
+     */
+    boolean mShouldOverrideDisplayConfiguration = true;
 
     /** Window tokens that are in the process of exiting, but still on screen for animations. */
     final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
@@ -558,6 +562,10 @@
                     w.updateLastInsetValues();
                 }
 
+                if (w.mAppToken != null) {
+                    w.mAppToken.layoutLetterbox(w);
+                }
+
                 if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
                         + " mDisplayFrame=" + w.mDisplayFrame);
@@ -697,7 +705,7 @@
 
         final AppWindowToken atoken = w.mAppToken;
         if (atoken != null) {
-            atoken.updateLetterbox(w);
+            atoken.updateLetterboxSurface(w);
             final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
             if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
                 mTmpUpdateAllDrawn.add(atoken);
@@ -1174,8 +1182,14 @@
             mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
         }
 
+        // We usually set the override info in DisplayManager so that we get consistent display
+        // metrics values when displays are changing and don't send out new values until WM is aware
+        // of them. However, we don't do this for displays that serve as containers for ActivityView
+        // because we don't want letter-/pillar-boxing during resize.
+        final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
+                ? mDisplayInfo : null;
         mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
-                mDisplayInfo);
+                overrideDisplayInfo);
 
         mBaseDisplayRect.set(0, 0, dw, dh);
 
@@ -3793,9 +3807,15 @@
         // we use relative layering of the IME targets child windows, and place the
         // IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
         //
+        // In the case the IME target is animating, the animation Z order may be different
+        // than the WindowContainer Z order, so it's difficult to be sure we have the correct
+        // IME target. In this case we just layer the IME over all transitions by placing it in the
+        // above applications layer.
+        //
         // In the case where we have no IME target we assign it where it's base layer would
         // place it in the AboveAppWindowContainers.
-        if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode()
+        if (imeTarget != null && !(imeTarget.inSplitScreenWindowingMode()
+                || imeTarget.mToken.isAppAnimating())
                 && (imeTarget.getSurfaceControl() != null)) {
             mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
                     // TODO: We need to use an extra level on the app surface to ensure
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 57693ac..dc6b491 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -19,7 +19,7 @@
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
-import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
+import static com.android.server.wm.DisplayFramesProto.STABLE_BOUNDS;
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b99e85f..5e2bb10 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -34,7 +34,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-import static com.android.server.wm.proto.DockedStackDividerControllerProto.MINIMIZED_DOCK;
+import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
 
 import android.content.Context;
 import android.content.res.Configuration;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 0f9735d..4eb021c 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -49,24 +49,26 @@
     }
 
     /**
-     * Sets the dimensions of the the letterbox, such that the area between the outer and inner
+     * Lays out the letterbox, such that the area between the outer and inner
      * frames will be covered by black color surfaces.
      *
-     * @param t     a transaction in which to set the dimensions
+     * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
+     *
      * @param outer the outer frame of the letterbox (this frame will be black, except the area
      *              that intersects with the {code inner} frame).
      * @param inner the inner frame of the letterbox (this frame will be clear)
      */
-    public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) {
+    public void layout(Rect outer, Rect inner) {
         mOuter.set(outer);
         mInner.set(inner);
 
-        mTop.setRect(t, outer.left, outer.top, inner.right, inner.top);
-        mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom);
-        mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom);
-        mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom);
+        mTop.layout(outer.left, outer.top, inner.right, inner.top);
+        mLeft.layout(outer.left, inner.top, inner.left, outer.bottom);
+        mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom);
+        mRight.layout(inner.right, outer.top, outer.right, inner.bottom);
     }
 
+
     /**
      * Gets the insets between the outer and inner rects.
      */
@@ -79,12 +81,20 @@
     }
 
     /**
+     * Returns true if any part of the letterbox overlaps with the given {@code rect}.
+     */
+    public boolean isOverlappingWith(Rect rect) {
+        return mTop.isOverlappingWith(rect) || mLeft.isOverlappingWith(rect)
+                || mBottom.isOverlappingWith(rect) || mRight.isOverlappingWith(rect);
+    }
+
+    /**
      * Hides the letterbox.
      *
-     * @param t a transaction in which to hide the letterbox
+     * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
      */
-    public void hide(SurfaceControl.Transaction t) {
-        setDimensions(t, EMPTY_RECT, EMPTY_RECT);
+    public void hide() {
+        layout(EMPTY_RECT, EMPTY_RECT);
     }
 
     /**
@@ -100,43 +110,40 @@
         mRight.destroy();
     }
 
+    /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
+    public boolean needsApplySurfaceChanges() {
+        return mTop.needsApplySurfaceChanges()
+                || mLeft.needsApplySurfaceChanges()
+                || mBottom.needsApplySurfaceChanges()
+                || mRight.needsApplySurfaceChanges();
+    }
+
+    public void applySurfaceChanges(SurfaceControl.Transaction t) {
+        mTop.applySurfaceChanges(t);
+        mLeft.applySurfaceChanges(t);
+        mBottom.applySurfaceChanges(t);
+        mRight.applySurfaceChanges(t);
+    }
+
     private class LetterboxSurface {
 
         private final String mType;
         private SurfaceControl mSurface;
 
-        private int mLastLeft = 0;
-        private int mLastTop = 0;
-        private int mLastRight = 0;
-        private int mLastBottom = 0;
+        private final Rect mSurfaceFrame = new Rect();
+        private final Rect mLayoutFrame = new Rect();
 
         public LetterboxSurface(String type) {
             mType = type;
         }
 
-        public void setRect(SurfaceControl.Transaction t,
-                int left, int top, int right, int bottom) {
-            if (mLastLeft == left && mLastTop == top
-                    && mLastRight == right && mLastBottom == bottom) {
+        public void layout(int left, int top, int right, int bottom) {
+            if (mLayoutFrame.left == left && mLayoutFrame.top == top
+                    && mLayoutFrame.right == right && mLayoutFrame.bottom == bottom) {
                 // Nothing changed.
                 return;
             }
-
-            if (left < right && top < bottom) {
-                if (mSurface == null) {
-                    createSurface();
-                }
-                t.setPosition(mSurface, left, top);
-                t.setSize(mSurface, right - left, bottom - top);
-                t.show(mSurface);
-            } else if (mSurface != null) {
-                t.hide(mSurface);
-            }
-
-            mLastLeft = left;
-            mLastTop = top;
-            mLastRight = right;
-            mLastBottom = bottom;
+            mLayoutFrame.set(left, top, right, bottom);
         }
 
         private void createSurface() {
@@ -154,11 +161,40 @@
         }
 
         public int getWidth() {
-            return Math.max(0, mLastRight - mLastLeft);
+            return Math.max(0, mLayoutFrame.width());
         }
 
         public int getHeight() {
-            return Math.max(0, mLastBottom - mLastTop);
+            return Math.max(0, mLayoutFrame.height());
+        }
+
+        public boolean isOverlappingWith(Rect rect) {
+            if (getWidth() <= 0 || getHeight() <= 0) {
+                return false;
+            }
+            return Rect.intersects(rect, mLayoutFrame);
+        }
+
+        public void applySurfaceChanges(SurfaceControl.Transaction t) {
+            if (mSurfaceFrame.equals(mLayoutFrame)) {
+                // Nothing changed.
+                return;
+            }
+            mSurfaceFrame.set(mLayoutFrame);
+            if (!mSurfaceFrame.isEmpty()) {
+                if (mSurface == null) {
+                    createSurface();
+                }
+                t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
+                t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
+                t.show(mSurface);
+            } else if (mSurface != null) {
+                t.hide(mSurface);
+            }
+        }
+
+        public boolean needsApplySurfaceChanges() {
+            return !mSurfaceFrame.equals(mLayoutFrame);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 3f1fde9..529aacc 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.proto.AnimationAdapterProto.LOCAL;
-import static com.android.server.wm.proto.LocalAnimationAdapterProto.ANIMATION_SPEC;
+import static com.android.server.wm.AnimationAdapterProto.LOCAL;
+import static com.android.server.wm.LocalAnimationAdapterProto.ANIMATION_SPEC;
 
 import android.os.SystemClock;
 import android.util.proto.ProtoOutputStream;
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 6966f1b..5f1916d 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -22,8 +22,8 @@
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.PinnedStackControllerProto.DEFAULT_BOUNDS;
-import static com.android.server.wm.proto.PinnedStackControllerProto.MOVEMENT_BOUNDS;
+import static com.android.server.wm.PinnedStackControllerProto.DEFAULT_BOUNDS;
+import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS;
 
 import android.app.RemoteAction;
 import android.content.pm.ParceledListSlice;
@@ -88,6 +88,8 @@
     private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
+    private boolean mIsShelfShowing;
+    private int mShelfHeight;
 
     // The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
     private ArrayList<RemoteAction> mActions = new ArrayList<>();
@@ -213,9 +215,11 @@
             listener.onListenerRegistered(mCallbacks);
             mPinnedStackListener = listener;
             notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
+            notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight);
             // The movement bounds notification needs to be sent before the minimized state, since
             // SystemUI may use the bounds to retore the minimized position
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
+                    false /* fromShelfAdjustment */);
             notifyActionsChanged(mActions);
             notifyMinimizeChanged(mIsMinimized);
         } catch (RemoteException e) {
@@ -297,7 +301,9 @@
                 mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
             } else {
                 Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
-                        0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+                        0, Math.max(mIsImeShowing ? mImeHeight : 0,
+                                mIsShelfShowing ? mShelfHeight : 0),
+                        defaultBounds);
             }
             return defaultBounds;
         }
@@ -310,7 +316,7 @@
      */
     synchronized void onDisplayInfoChanged() {
         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
-        notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+        notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
     }
 
     /**
@@ -342,14 +348,15 @@
             // Calculate the stack bounds in the new orientation to the same same fraction along the
             // rotated movement bounds.
             final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
-                    false /* adjustForIme */);
+                    false /* adjustForIme */, false /* adjustForShelf */);
             mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                     snapFraction);
             if (mIsMinimized) {
                 applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
             }
 
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
+                    false /* fromShelfAdjustment */);
 
             outBounds.set(postChangeStackBounds);
             return true;
@@ -373,7 +380,22 @@
         mIsImeShowing = imeShowing;
         mImeHeight = imeHeight;
         notifyImeVisibilityChanged(imeShowing, imeHeight);
-        notifyMovementBoundsChanged(true /* fromImeAdjustment */);
+        notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+    }
+
+    /**
+     * Sets the shelf state and height.
+     */
+    void setAdjustedForShelf(boolean adjustedForShelf, int shelfHeight) {
+        final boolean shelfShowing = adjustedForShelf && shelfHeight > 0;
+        if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
+            return;
+        }
+
+        mIsShelfShowing = shelfShowing;
+        mShelfHeight = shelfHeight;
+        notifyShelfVisibilityChanged(shelfShowing, shelfHeight);
+        notifyMovementBoundsChanged(false /* fromImeAdjustment */, true /* fromShelfAdjustment */);
     }
 
     /**
@@ -382,7 +404,8 @@
     void setAspectRatio(float aspectRatio) {
         if (Float.compare(mAspectRatio, aspectRatio) != 0) {
             mAspectRatio = aspectRatio;
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
+                    false /* fromShelfAdjustment */);
         }
     }
 
@@ -417,6 +440,16 @@
         }
     }
 
+    private void notifyShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+        if (mPinnedStackListener != null) {
+            try {
+                mPinnedStackListener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
+            }
+        }
+    }
+
     /**
      * Notifies listeners that the PIP minimized state has changed.
      */
@@ -446,7 +479,8 @@
     /**
      * Notifies listeners that the PIP movement bounds have changed.
      */
-    private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
+    private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
+            boolean fromShelfAdjustment) {
         synchronized (mService.mWindowMap) {
             if (mPinnedStackListener == null) {
                 return;
@@ -467,7 +501,8 @@
                     animatingBounds.set(normalBounds);
                 }
                 mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
-                        animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
+                        animatingBounds, fromImeAdjustment, fromShelfAdjustment,
+                        mDisplayInfo.rotation);
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
@@ -493,7 +528,8 @@
      */
     private Rect getMovementBounds(Rect stackBounds) {
         synchronized (mService.mWindowMap) {
-            return getMovementBounds(stackBounds, true /* adjustForIme */);
+            return getMovementBounds(stackBounds, true /* adjustForIme */,
+                    true /* adjustForShelf */);
         }
     }
 
@@ -501,14 +537,15 @@
      * @return the movement bounds for the given {@param stackBounds} and the current state of the
      *         controller.
      */
-    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme, boolean adjustForShelf) {
         synchronized (mService.mWindowMap) {
             final Rect movementBounds = new Rect();
             getInsetBounds(movementBounds);
 
             // Apply the movement bounds adjustments based on the current state
             mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
-                    (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+                    Math.max((adjustForIme && mIsImeShowing) ? mImeHeight : 0,
+                            (adjustForShelf && mIsShelfShowing) ? mShelfHeight : 0));
             return movementBounds;
         }
     }
@@ -549,6 +586,9 @@
         pw.print(prefix + "  movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
         pw.println();
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
+        pw.println(prefix + "  mImeHeight=" + mImeHeight);
+        pw.println(prefix + "  mIsShelfShowing=" + mIsShelfShowing);
+        pw.println(prefix + "  mShelfHeight=" + mShelfHeight);
         pw.println(prefix + "  mIsMinimized=" + mIsMinimized);
         if (mActions.isEmpty()) {
             pw.println(prefix + "  mActions=[]");
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 3e72a71..19d6691 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -26,8 +26,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
-import static com.android.server.wm.proto.RemoteAnimationAdapterWrapperProto.TARGET;
-import static com.android.server.wm.proto.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
 
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.WindowConfiguration;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d645110..c590067 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -19,8 +19,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.proto.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -99,6 +99,10 @@
         mFinishedCallback = new FinishedCallback(this);
 
         final RemoteAnimationTarget[] animations = createAnimations();
+        if (animations.length == 0) {
+            onAnimationFinished();
+            return;
+        }
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
                 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
@@ -132,6 +136,8 @@
                     mPendingAnimations.get(i).createRemoteAppAnimation();
             if (target != null) {
                 targets.add(target);
+            } else {
+                mPendingAnimations.remove(i);
             }
         }
         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
@@ -225,10 +231,8 @@
         RemoteAnimationTarget createRemoteAppAnimation() {
             final Task task = mAppWindowToken.getTask();
             final WindowState mainWindow = mAppWindowToken.findMainWindow();
-            if (task == null) {
-                return null;
-            }
-            if (mainWindow == null) {
+            if (task == null || mainWindow == null || mCapturedFinishCallback == null
+                    || mCapturedLeash == null) {
                 return null;
             }
             mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 32ae523..52d8177 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -84,9 +84,9 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.proto.RootWindowContainerProto.DISPLAYS;
-import static com.android.server.wm.proto.RootWindowContainerProto.WINDOWS;
-import static com.android.server.wm.proto.RootWindowContainerProto.WINDOW_CONTAINER;
+import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
+import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
+import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
 
 /** Root {@link WindowContainer} for the device. */
 class RootWindowContainer extends WindowContainer<DisplayContent> {
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index ad2fabb..fa8a5c6 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -23,12 +23,13 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
-import static com.android.server.wm.proto.ScreenRotationAnimationProto.ANIMATION_RUNNING;
-import static com.android.server.wm.proto.ScreenRotationAnimationProto.STARTED;
+import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
+import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
 
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -268,15 +269,23 @@
                     .build();
 
             // capture a screenshot into the surface we just created
-            Surface sur = new Surface();
-            sur.copyFrom(mSurfaceControl);
             // TODO(multidisplay): we should use the proper display
-            SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
-                            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
-            t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
-            t.setAlpha(mSurfaceControl, 0);
-            t.show(mSurfaceControl);
-            sur.destroy();
+            final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
+            final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
+            // This null check below is to guard a race condition where WMS didn't have a chance to
+            // respond to display disconnection before handling rotation , that surfaceflinger may
+            // return a null handle here because it doesn't think that display is valid anymore.
+            if (displayHandle != null) {
+                Surface sur = new Surface();
+                sur.copyFrom(mSurfaceControl);
+                SurfaceControl.screenshot(displayHandle, sur);
+                t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
+                t.setAlpha(mSurfaceControl, 0);
+                t.show(mSurfaceControl);
+                sur.destroy();
+            } else {
+                Slog.w(TAG, "Built-in display " + displayId + " is null.");
+            }
         } catch (OutOfResourcesException e) {
             Slog.w(TAG, "Unable to allocate freeze surface", e);
         }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c06caaf..ba3d091 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -19,9 +19,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.SurfaceAnimatorProto.ANIMATION_ADAPTER;
-import static com.android.server.wm.proto.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
-import static com.android.server.wm.proto.SurfaceAnimatorProto.LEASH;
+import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
+import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
+import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -81,9 +81,14 @@
                 if (anim != mAnimation) {
                     return;
                 }
-                reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
-                if (animationFinishedCallback != null) {
-                    animationFinishedCallback.run();
+                final Runnable resetAndInvokeFinish = () -> {
+                    reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
+                    if (animationFinishedCallback != null) {
+                        animationFinishedCallback.run();
+                    }
+                };
+                if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
+                    resetAndInvokeFinish.run();
                 }
             }
         };
@@ -229,6 +234,10 @@
         mService.mAnimationTransferMap.put(mAnimation, this);
     }
 
+    boolean isAnimationStartDelayed() {
+        return mAnimationStartDelayed;
+    }
+
     /**
      * Cancels the animation, and resets the leash.
      *
@@ -305,7 +314,7 @@
 
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
-     * com.android.server.wm.proto.SurfaceAnimatorProto}.
+     * com.android.server.wm.SurfaceAnimatorProto}.
      *
      * @param proto Stream to write the SurfaceAnimator object to.
      * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message.
@@ -407,5 +416,17 @@
          * @return The height of the surface to be animated.
          */
         int getSurfaceHeight();
+
+        /**
+         * Gets called when the animation is about to finish and gives the client the opportunity to
+         * defer finishing the animation, i.e. it keeps the leash around until the client calls
+         * {@link #cancelAnimation}.
+         *
+         * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
+         * @return Whether the client would like to defer the animation finish.
+         */
+        default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            return false;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a403e6f2..e4722f9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -26,13 +26,13 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.TaskProto.APP_WINDOW_TOKENS;
-import static com.android.server.wm.proto.TaskProto.BOUNDS;
-import static com.android.server.wm.proto.TaskProto.DEFER_REMOVAL;
-import static com.android.server.wm.proto.TaskProto.FILLS_PARENT;
-import static com.android.server.wm.proto.TaskProto.ID;
-import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS;
-import static com.android.server.wm.proto.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.ID;
+import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
+import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
 import android.app.ActivityManager.TaskDescription;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b5d00a7..900e2df 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -35,18 +35,18 @@
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.StackProto.ADJUSTED_BOUNDS;
-import static com.android.server.wm.proto.StackProto.ADJUSTED_FOR_IME;
-import static com.android.server.wm.proto.StackProto.ADJUST_DIVIDER_AMOUNT;
-import static com.android.server.wm.proto.StackProto.ADJUST_IME_AMOUNT;
-import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
-import static com.android.server.wm.proto.StackProto.BOUNDS;
-import static com.android.server.wm.proto.StackProto.DEFER_REMOVAL;
-import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
-import static com.android.server.wm.proto.StackProto.ID;
-import static com.android.server.wm.proto.StackProto.MINIMIZE_AMOUNT;
-import static com.android.server.wm.proto.StackProto.TASKS;
-import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER;
+import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
+import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
+import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
+import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
+import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
+import static com.android.server.wm.StackProto.BOUNDS;
+import static com.android.server.wm.StackProto.DEFER_REMOVAL;
+import static com.android.server.wm.StackProto.FILLS_PARENT;
+import static com.android.server.wm.StackProto.ID;
+import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
+import static com.android.server.wm.StackProto.TASKS;
+import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
@@ -155,6 +155,9 @@
     final Rect mTmpDimBoundsRect = new Rect();
     private final Point mLastSurfaceSize = new Point();
 
+    private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
+            new AnimatingAppWindowTokenRegistry();
+
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         super(service);
         mStackId = stackId;
@@ -1782,4 +1785,8 @@
         outPos.x -= outset;
         outPos.y -= outset;
     }
+
+    AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
+        return mAnimatingAppWindowTokenRegistry;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index a41eba8..7b7cb30 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -19,8 +19,8 @@
 import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
-import static com.android.server.wm.proto.AnimationSpecProto.WINDOW;
-import static com.android.server.wm.proto.WindowAnimationSpecProto.ANIMATION;
+import static com.android.server.wm.AnimationSpecProto.WINDOW;
+import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
 
 import android.graphics.Point;
 import android.graphics.Rect;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5ae4dc5..28fdaae 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -23,10 +23,10 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
-import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
-import static com.android.server.wm.proto.WindowContainerProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
+import static com.android.server.wm.WindowContainerProto.ORIENTATION;
+import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.WindowContainerProto.VISIBLE;
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
@@ -285,7 +285,14 @@
         }
 
         if (mSurfaceControl != null) {
-            getPendingTransaction().destroy(mSurfaceControl);
+            mPendingTransaction.destroy(mSurfaceControl);
+
+            // Merge to parent transaction to ensure the transactions on this WindowContainer are
+            // applied in native even if WindowContainer is removed.
+            if (mParent != null) {
+                mParent.getPendingTransaction().merge(mPendingTransaction);
+            }
+
             mSurfaceControl = null;
             scheduleAnimation();
         }
@@ -980,7 +987,7 @@
 
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
-     * {@link com.android.server.wm.proto.WindowContainerProto}.
+     * {@link com.android.server.wm.WindowContainerProto}.
      *
      * @param proto     Stream to write the WindowContainer object to.
      * @param fieldId   Field Id of the WindowContainer as defined in the parent message.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ea84b22..56b314f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -48,6 +48,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -97,15 +98,15 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.APP_TRANSITION;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_APP;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.LAST_ORIENTATION;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.POLICY;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
-import static com.android.server.wm.proto.WindowManagerServiceDumpProto.ROTATION;
+import static com.android.server.wm.WindowManagerServiceDumpProto.APP_TRANSITION;
+import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
+import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP;
+import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
+import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
+import static com.android.server.wm.WindowManagerServiceDumpProto.LAST_ORIENTATION;
+import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
+import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -1117,17 +1118,7 @@
                 throw new IllegalStateException("Display has not been initialialized");
             }
 
-            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-
-            // Adding a window is an exception where the WindowManagerService can create the
-            // display instead of waiting for the ActivityManagerService to drive creation.
-            if (displayContent == null) {
-                final Display display = mDisplayManager.getDisplay(displayId);
-
-                if (display != null) {
-                    displayContent = mRoot.createDisplayContent(display, null /* controller */);
-                }
-            }
+            final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
 
             if (displayContent == null) {
                 Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
@@ -1224,8 +1215,10 @@
                     }
                 }
                 final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+                final boolean isRoundedCornerOverlay =
+                        (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
                 token = new WindowToken(this, binder, type, false, displayContent,
-                        session.mCanAddInternalSystemWindow);
+                        session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
             } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                 atoken = token.asAppWindowToken();
                 if (atoken == null) {
@@ -1493,6 +1486,32 @@
         return res;
     }
 
+    /**
+     * Get existing {@link DisplayContent} or create a new one if the display is registered in
+     * DisplayManager.
+     *
+     * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent}
+     * that corresponds to a display just added to DisplayManager has not yet been created. This
+     * usually means that the call of this method was initiated from outside of Activity or Window
+     * Manager. In most cases the regular getter should be used.
+     * @see RootWindowContainer#getDisplayContent(int)
+     */
+    private DisplayContent getDisplayContentOrCreate(int displayId) {
+        DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+
+        // Create an instance if possible instead of waiting for the ActivityManagerService to drive
+        // the creation.
+        if (displayContent == null) {
+            final Display display = mDisplayManager.getDisplay(displayId);
+
+            if (display != null) {
+                displayContent = mRoot.createDisplayContent(display, null /* controller */);
+            }
+        }
+
+        return displayContent;
+    }
+
     private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
             WindowState attachedWindow) {
         // Try using the target SDK of the root window
@@ -5892,6 +5911,16 @@
     }
 
     @Override
+    public void setShelfHeight(boolean visible, int shelfHeight) {
+        mAmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+                "setShelfHeight()");
+        synchronized (mWindowMap) {
+            getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
+                    shelfHeight);
+        }
+    }
+
+    @Override
     public void statusBarVisibilityChanged(int visibility) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -6153,7 +6182,7 @@
 
     /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
-     * {@link com.android.server.wm.proto.WindowManagerServiceDumpProto}.
+     * {@link com.android.server.wm.WindowManagerServiceDumpProto}.
      *
      * @param proto     Stream to write the WindowContainer object to.
      * @param trim      If true, reduce the amount of data written.
@@ -6977,6 +7006,24 @@
     }
 
     @Override
+    public void dontOverrideDisplayInfo(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent dc = getDisplayContentOrCreate(displayId);
+            if (dc == null) {
+                throw new IllegalArgumentException(
+                        "Trying to register a non existent display.");
+            }
+            // We usually set the override info in DisplayManager so that we get consistent
+            // values when displays are changing. However, we don't do this for displays that
+            // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
+            // during resize.
+            dc.mShouldOverrideDisplayConfiguration = false;
+            mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
+                    null /* info */);
+        }
+    }
+
+    @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
         if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 91cd4bb..993556f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -24,7 +24,6 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.SurfaceControl.Transaction;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -35,7 +34,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -46,7 +44,6 @@
 import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 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.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -110,50 +107,50 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
-import static com.android.server.wm.proto.IdentifierProto.TITLE;
-import static com.android.server.wm.proto.IdentifierProto.USER_ID;
-import static com.android.server.wm.proto.AnimationSpecProto.MOVE;
-import static com.android.server.wm.proto.MoveAnimationSpecProto.DURATION;
-import static com.android.server.wm.proto.MoveAnimationSpecProto.FROM;
-import static com.android.server.wm.proto.MoveAnimationSpecProto.TO;
-import static com.android.server.wm.proto.WindowStateProto.ANIMATING_EXIT;
-import static com.android.server.wm.proto.WindowStateProto.ANIMATOR;
-import static com.android.server.wm.proto.WindowStateProto.ATTRIBUTES;
-import static com.android.server.wm.proto.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.proto.WindowStateProto.CONTAINING_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.CONTENT_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.CONTENT_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.CUTOUT;
-import static com.android.server.wm.proto.WindowStateProto.DECOR_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.DESTROYING;
-import static com.android.server.wm.proto.WindowStateProto.DISPLAY_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.proto.WindowStateProto.FRAME;
-import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
-import static com.android.server.wm.proto.WindowStateProto.IS_ON_SCREEN;
-import static com.android.server.wm.proto.WindowStateProto.IS_READY_FOR_DISPLAY;
-import static com.android.server.wm.proto.WindowStateProto.IS_VISIBLE;
-import static com.android.server.wm.proto.WindowStateProto.OUTSETS;
-import static com.android.server.wm.proto.WindowStateProto.OUTSET_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.REMOVED;
-import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT;
-import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT;
-import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
-import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION;
-import static com.android.server.wm.proto.WindowStateProto.SYSTEM_UI_VISIBILITY;
-import static com.android.server.wm.proto.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.proto.WindowStateProto.VISIBLE_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.VISIBLE_INSETS;
-import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.AnimationSpecProto.MOVE;
+import static com.android.server.wm.MoveAnimationSpecProto.DURATION;
+import static com.android.server.wm.MoveAnimationSpecProto.FROM;
+import static com.android.server.wm.MoveAnimationSpecProto.TO;
+import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
+import static com.android.server.wm.WindowStateProto.ANIMATOR;
+import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
+import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
+import static com.android.server.wm.WindowStateProto.CONTAINING_FRAME;
+import static com.android.server.wm.WindowStateProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
+import static com.android.server.wm.WindowStateProto.CUTOUT;
+import static com.android.server.wm.WindowStateProto.DECOR_FRAME;
+import static com.android.server.wm.WindowStateProto.DESTROYING;
+import static com.android.server.wm.WindowStateProto.DISPLAY_FRAME;
+import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
+import static com.android.server.wm.WindowStateProto.FRAME;
+import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
+import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
+import static com.android.server.wm.WindowStateProto.IDENTIFIER;
+import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
+import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
+import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
+import static com.android.server.wm.WindowStateProto.OUTSETS;
+import static com.android.server.wm.WindowStateProto.OUTSET_FRAME;
+import static com.android.server.wm.WindowStateProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
+import static com.android.server.wm.WindowStateProto.PARENT_FRAME;
+import static com.android.server.wm.WindowStateProto.REMOVED;
+import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
+import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
+import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
+import static com.android.server.wm.WindowStateProto.STABLE_INSETS;
+import static com.android.server.wm.WindowStateProto.STACK_ID;
+import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
+import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
+import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
+import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
+import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
+import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
+import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
 import android.app.AppOpsManager;
@@ -410,6 +407,9 @@
 
     private final Rect mParentFrame = new Rect();
 
+    /** Whether the parent frame would have been different if there was no display cutout. */
+    private boolean mParentFrameWasClippedByDisplayCutout;
+
     // The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
     // screen area of the device.
     final Rect mDisplayFrame = new Rect();
@@ -839,7 +839,8 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            Rect outsetFrame, WmDisplayCutout displayCutout) {
+            Rect outsetFrame, WmDisplayCutout displayCutout,
+            boolean parentFrameWasClippedByDisplayCutout) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -848,6 +849,7 @@
             return;
         }
         mHaveFrame = true;
+        mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;
 
         final Task task = getTask();
         final boolean inFullscreenContainer = inFullscreenContainer();
@@ -857,10 +859,12 @@
         // If the task has temp inset bounds set, we have to make sure all its windows uses
         // the temp inset frame. Otherwise different display frames get applied to the main
         // window and the child window, making them misaligned.
-        if (inFullscreenContainer || isLetterboxedAppWindow()) {
-            mInsetFrame.setEmpty();
-        } else if (task != null && isInMultiWindowMode()) {
+        // Otherwise we need to clear the inset frame, to avoid using a stale frame after leaving
+        // multi window mode.
+        if (task != null && isInMultiWindowMode()) {
             task.getTempInsetBounds(mInsetFrame);
+        } else {
+            mInsetFrame.setEmpty();
         }
 
         // Denotes the actual frame used to calculate the insets and to perform the layout. When
@@ -1030,15 +1034,21 @@
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
             // For the docked divider, we calculate the stable insets like a full-screen window
             // so it can use it to calculate the snap positions.
-            mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0),
-                    Math.max(mStableFrame.top - mDisplayFrame.top, 0),
-                    Math.max(mDisplayFrame.right - mStableFrame.right, 0),
-                    Math.max(mDisplayFrame.bottom - mStableFrame.bottom, 0));
+            final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame);
+            mTmpRect.set(mDisplayFrame);
+            mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
+            mTmpRect.intersectUnchecked(mStableFrame);
+
+            mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0),
+                    Math.max(mTmpRect.top - mDisplayFrame.top, 0),
+                    Math.max(mDisplayFrame.right - mTmpRect.right, 0),
+                    Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0));
 
             // The divider doesn't care about insets in any case, so set it to empty so we don't
             // trigger a relayout when moving it.
             mContentInsets.setEmpty();
             mVisibleInsets.setEmpty();
+            displayCutout = WmDisplayCutout.NO_CUTOUT;
         } else {
             getDisplayContent().getBounds(mTmpRect);
             // Override right and/or bottom insets in case if the frame doesn't fit the screen in
@@ -3055,23 +3065,36 @@
             // Only windows with an AppWindowToken are letterboxed.
             return false;
         }
-        if (getDisplayContent().getDisplayInfo().displayCutout == null) {
-            // No cutout, no letterbox.
+        if (!mParentFrameWasClippedByDisplayCutout) {
+            // Cutout didn't make a difference, no letterbox
             return false;
         }
         if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
             // Layout in cutout, no letterbox.
             return false;
         }
-        // TODO: handle dialogs and other non-filling windows
-        if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
-            // Never layout in cutout, always letterbox.
-            return true;
+        if (!mAttrs.isFullscreen()) {
+            // Not filling the parent frame, no letterbox
+            return false;
         }
-        // Letterbox if any fullscreen mode is set.
-        final int fl = mAttrs.flags;
-        final int sysui = mSystemUiVisibility;
-        return (fl & FLAG_FULLSCREEN) != 0 || (sysui & (SYSTEM_UI_FLAG_FULLSCREEN)) != 0;
+        // Otherwise we need a letterbox if the layout was smaller than the app window token allowed
+        // it to be.
+        return !frameCoversEntireAppTokenBounds();
+    }
+
+    /**
+     * @return true if this window covers the entire bounds of its app window token
+     * @throws NullPointerException if there is no app window token for this window
+     */
+    private boolean frameCoversEntireAppTokenBounds() {
+        mTmpRect.set(mAppToken.getBounds());
+        mTmpRect.intersectUnchecked(mFrame);
+        return mAppToken.getBounds().equals(mTmpRect);
+    }
+
+    @Override
+    public boolean isLetterboxedOverlappingWith(Rect rect) {
+        return mAppToken != null && mAppToken.isLetterboxOverlappingWith(rect);
     }
 
     boolean isDragResizeChanged() {
@@ -4510,8 +4533,7 @@
                 mAttrs.type == TYPE_NAVIGATION_BAR ||
                 // It's tempting to wonder: Have we forgotten the rounded corners overlay?
                 // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL
-                mAttrs.type == TYPE_NAVIGATION_BAR_PANEL ||
-                mAttrs.type == TYPE_STATUS_BAR) {
+                mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 410eddf..e92d460 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,10 +41,10 @@
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.proto.WindowStateAnimatorProto.DRAW_STATE;
-import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT;
-import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE;
-import static com.android.server.wm.proto.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
+import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT;
+import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
+import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -225,6 +225,11 @@
     // case we need to give the client a new Surface if it lays back out to a visible state.
     boolean mChildrenDetached = false;
 
+    // Set to true after the first frame of the Pinned stack animation
+    // and reset after the last to ensure we only reset mForceScaleUntilResize
+    // once per animation.
+    boolean mPipAnimationStarted = false;
+
     WindowStateAnimator(final WindowState win) {
         final WindowManagerService service = win.mService;
 
@@ -512,7 +517,7 @@
             mDrawState = NO_SURFACE;
             return null;
         } catch (Exception e) {
-            Slog.e(TAG, "Exception creating surface", e);
+            Slog.e(TAG, "Exception creating surface (parent dead?)", e);
             mDrawState = NO_SURFACE;
             return null;
         }
@@ -983,8 +988,13 @@
             // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
             // then take over the scaling until the new buffer arrives, and things
             // will be seamless.
-            mForceScaleUntilResize = true;
+            if (mPipAnimationStarted == false) {
+                mForceScaleUntilResize = true;
+                mPipAnimationStarted = true;
+            }
         } else {
+            mPipAnimationStarted = false;
+
             if (!w.mSeamlesslyRotated) {
                 mSurfaceController.setPositionInTransaction(mXOffset, mYOffset, recoveringMemory);
             }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f6c0a54..66c8cca 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -25,8 +25,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.proto.WindowSurfaceControllerProto.LAYER;
-import static com.android.server.wm.proto.WindowSurfaceControllerProto.SHOWN;
+import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
+import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
 import android.graphics.Point;
 import android.graphics.PointF;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 172efdc..14680d9 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,18 +17,19 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE;
-import static com.android.server.wm.proto.WindowTokenProto.HIDDEN;
-import static com.android.server.wm.proto.WindowTokenProto.PAUSED;
-import static com.android.server.wm.proto.WindowTokenProto.WAITING_TO_SHOW;
-import static com.android.server.wm.proto.WindowTokenProto.WINDOWS;
-import static com.android.server.wm.proto.WindowTokenProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.WindowTokenProto.HIDDEN;
+import static com.android.server.wm.WindowTokenProto.PAUSED;
+import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
+import static com.android.server.wm.WindowTokenProto.WINDOWS;
+import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
 import android.os.Debug;
@@ -53,6 +54,9 @@
     // The type of window this token is for, as per WindowManager.LayoutParams.
     final int windowType;
 
+    /** {@code true} if this holds the rounded corner overlay */
+    final boolean mRoundedCornerOverlay;
+
     // Set if this token was explicitly added by a client, so should
     // persist (not be removed) when all windows are removed.
     boolean mPersistOnEmpty;
@@ -105,11 +109,18 @@
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
             DisplayContent dc, boolean ownerCanManageAppTokens) {
+        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+                false /* roundedCornersOverlay */);
+    }
+
+    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
+            DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
         super(service);
         token = _token;
         windowType = type;
         mPersistOnEmpty = persistOnEmpty;
         mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+        mRoundedCornerOverlay = roundedCornerOverlay;
         onDisplayChanged(dc);
     }
 
@@ -259,6 +270,12 @@
         dc.reParentWindowToken(this);
         mDisplayContent = dc;
 
+        // The rounded corner overlay should not be rotated. We ensure that by moving it outside
+        // the windowing layer.
+        if (mRoundedCornerOverlay) {
+            mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
+        }
+
         // TODO(b/36740756): One day this should perhaps be hooked
         // up with goodToGo, so we don't move a window
         // to another display before the window behind
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index a299781..8fa56bb 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -17,13 +17,13 @@
 package com.android.server.wm;
 
 import static android.os.Build.IS_USER;
-import static com.android.server.wm.proto.WindowManagerTraceFileProto.ENTRY;
-import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER;
-import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
-import static com.android.server.wm.proto.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
-import static com.android.server.wm.proto.WindowManagerTraceProto.WHERE;
-import static com.android.server.wm.proto.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
+import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.wm.WindowManagerTraceProto.WHERE;
+import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
 
 import android.content.Context;
 import android.os.ShellCommand;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index cf42c0c..dcee151 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -125,6 +125,9 @@
     }
 };
 
+// Must match the value from GnssMeasurement.java
+static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
 sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
 sp<IGnss_V1_0> gnssHal = nullptr;
 sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
@@ -807,7 +810,8 @@
     SET(PseudorangeRateUncertaintyMetersPerSecond,
         measurement->pseudorangeRateUncertaintyMps);
     SET(AccumulatedDeltaRangeState,
-        (static_cast<int32_t>(measurement->accumulatedDeltaRangeState)));
+        (static_cast<int32_t>(measurement->accumulatedDeltaRangeState) &
+        !ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0
     SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM);
     SET(AccumulatedDeltaRangeUncertaintyMeters,
         measurement->accumulatedDeltaRangeUncertaintyM);
@@ -888,9 +892,10 @@
         if (measurements_v1_1 != NULL) {
             translateGnssMeasurement_V1_0(env, &(measurements_v1_1[i].v1_0), object);
 
-            // Set the V1_1 flag
+            // Set the V1_1 flag, and mark that new field has valid information for Java Layer
             SET(AccumulatedDeltaRangeState,
-                    static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState));
+                    (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
+                    ADR_STATE_HALF_CYCLE_REPORTED));
         } else {
             translateGnssMeasurement_V1_0(env, &(measurements_v1_0[i]), object);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5a3b268..2e07703 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -107,7 +107,9 @@
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
+import android.app.backup.BackupManager;
 import android.app.backup.IBackupManager;
+import android.app.backup.ISelectBackupTransportCallback;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
@@ -252,6 +254,7 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
@@ -6465,8 +6468,11 @@
     }
 
     /**
-     * Set the storage encryption request for a single admin.  Returns the new total request
-     * status (for all admins).
+     * Called by an application that is administering the device to request that the storage system
+     * be encrypted. Does nothing if the caller is on a secondary user or a managed profile.
+     *
+     * @return the new total request status (for all admins), or {@link
+     *         DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} if called for a non-system user
      */
     @Override
     public int setStorageEncryption(ComponentName who, boolean encrypt) {
@@ -6481,7 +6487,7 @@
             if (userHandle != UserHandle.USER_SYSTEM) {
                 Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User "
                         + UserHandle.getCallingUserId() + " is not permitted.");
-                return 0;
+                return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
             }
 
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
@@ -9246,7 +9252,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 return mIPackageManager.setPackagesSuspendedAsUser(
-                        packageNames, suspended, callingUserId);
+                        packageNames, suspended, null, null, "android", callingUserId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
                 Slog.e(LOG_TAG, "Failed talking to the package manager", re);
@@ -11994,20 +12000,32 @@
     }
 
     @Override
-    public void setMandatoryBackupTransport(
-            ComponentName admin, ComponentName backupTransportComponent) {
+    public boolean setMandatoryBackupTransport(
+            ComponentName admin,
+            ComponentName backupTransportComponent) {
         if (!mHasFeature) {
-            return;
+            return false;
         }
         Preconditions.checkNotNull(admin);
         synchronized (this) {
-            ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-            if (!Objects.equals(backupTransportComponent, activeAdmin.mandatoryBackupTransport)) {
-                activeAdmin.mandatoryBackupTransport = backupTransportComponent;
-                saveSettingsLocked(UserHandle.USER_SYSTEM);
-            }
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
         }
+
+        final int callingUid = mInjector.binderGetCallingUid();
+        final AtomicBoolean success = new AtomicBoolean(false);
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        final ISelectBackupTransportCallback selectBackupTransportCallbackInternal =
+                new ISelectBackupTransportCallback.Stub() {
+                    public void onSuccess(String transportName) {
+                        saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
+                        success.set(true);
+                        countDownLatch.countDown();
+                    }
+
+                    public void onFailure(int reason) {
+                        countDownLatch.countDown();
+                    }
+                };
         final long identity = mInjector.binderClearCallingIdentity();
         try {
             IBackupManager ibm = mInjector.getIBackupManager();
@@ -12015,14 +12033,39 @@
                 if (!ibm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
                     ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
                 }
-                ibm.selectBackupTransportAsync(backupTransportComponent, null);
-                ibm.setBackupEnabled(true);
+                ibm.selectBackupTransportAsync(
+                        backupTransportComponent, selectBackupTransportCallbackInternal);
+                countDownLatch.await();
+                if (success.get()) {
+                    ibm.setBackupEnabled(true);
+                }
+            } else if (backupTransportComponent == null) {
+                saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
+                success.set(true);
             }
         } catch (RemoteException e) {
             throw new IllegalStateException("Failed to set mandatory backup transport.", e);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("Failed to set mandatory backup transport.", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(identity);
         }
+        return success.get();
+    }
+
+    synchronized private void saveMandatoryBackupTransport(
+            ComponentName admin, int callingUid, ComponentName backupTransportComponent) {
+        ActiveAdmin activeAdmin =
+                getActiveAdminWithPolicyForUidLocked(
+                        admin,
+                        DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                        callingUid);
+        if (!Objects.equals(backupTransportComponent,
+                activeAdmin.mandatoryBackupTransport)) {
+            activeAdmin.mandatoryBackupTransport =
+                    backupTransportComponent;
+            saveSettingsLocked(UserHandle.USER_SYSTEM);
+        }
     }
 
     @Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 70abf80..6f50ee2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -196,6 +196,8 @@
             "com.android.server.search.SearchManagerService$Lifecycle";
     private static final String THERMAL_OBSERVER_CLASS =
             "com.google.android.clockwork.ThermalObserver";
+    private static final String WEAR_CONFIG_SERVICE_CLASS =
+            "com.google.android.clockwork.WearConfigManagerService";
     private static final String WEAR_CONNECTIVITY_SERVICE_CLASS =
             "com.android.clockwork.connectivity.WearConnectivityService";
     private static final String WEAR_SIDEKICK_SERVICE_CLASS =
@@ -703,6 +705,11 @@
             mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
             traceEnd();
         }
+
+        // Tracks cpu time spent in binder calls
+        traceBeginAndSlog("StartBinderCallsStatsService");
+        BinderCallsStatsService.start();
+        traceEnd();
     }
 
     /**
@@ -1543,6 +1550,10 @@
         }
 
         if (isWatch) {
+            traceBeginAndSlog("StartWearConfigService");
+            mSystemServiceManager.startService(WEAR_CONFIG_SERVICE_CLASS);
+            traceEnd();
+
             traceBeginAndSlog("StartWearConnectivityService");
             mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
             traceEnd();
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 0ca0a1a..cdb339a 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -36,6 +36,7 @@
 LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
     aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
 LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
+LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
 
 LOCAL_JAVA_LIBRARIES := \
     android.hidl.manager-V1.0-java \
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 372b8cc..ce98d65 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -62,6 +62,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.SUSPEND_APPS"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 23e5072..082827c 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -15,9 +15,11 @@
 -->
 <configuration description="Runs Frameworks Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksServicesTests.apk" />
         <option name="test-file-name" value="JobTestApp.apk" />
         <option name="test-file-name" value="ConnTestApp.apk" />
+        <option name="test-file-name" value="SuspendTestApp.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 0462b14..e5c6c6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.accessibility;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -57,6 +60,7 @@
     static final int SERVICE_ID = 42;
 
     AccessibilityServiceConnection mConnection;
+
     @Mock AccessibilityManagerService.UserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
@@ -66,7 +70,9 @@
     @Mock WindowManagerInternal mMockWindowManagerInternal;
     @Mock GlobalActionPerformer mMockGlobalActionPerformer;
     @Mock KeyEventDispatcher mMockKeyEventDispatcher;
+    @Mock MagnificationController mMockMagnificationController;
 
+    MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
 
     @BeforeClass
     public static void oneTimeInitialization() {
@@ -79,12 +85,15 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
+        when(mMockSystemSupport.getMagnificationController())
+                .thenReturn(mMockMagnificationController);
+
         when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
         mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
 
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
-                COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, new Handler(), new Object(),
+                COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
                 mMockGlobalActionPerformer);
     }
@@ -106,12 +115,30 @@
     @Test
     public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException {
         IBinder mockBinder = mock(IBinder.class);
-        when(mMockUserState.getBindingServicesLocked())
-                .thenReturn(new HashSet<>(Arrays.asList(COMPONENT_NAME)));
+        setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
         verify(mockBinder).linkToDeath(eq(mConnection), anyInt());
         mConnection.unbindLocked();
         verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt());
     }
+
+    @Test
+    public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() {
+        IBinder mockBinder = mock(IBinder.class);
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        assertFalse(mConnection.getServiceInfo().crashed);
+        mConnection.binderDied();
+        assertTrue(mConnection.getServiceInfo().crashed);
+        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mHandler.sendAllMessages();
+        assertFalse(mConnection.getServiceInfo().crashed);
+    }
+
+    private void setServiceBinding(ComponentName componentName) {
+        when(mMockUserState.getBindingServicesLocked())
+                .thenReturn(new HashSet<>(Arrays.asList(componentName)));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 2378e6d..b452ea5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -22,23 +22,39 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.any;
 
 import android.app.ActivityManager;
 import android.app.WaitResult;
 import android.content.ComponentName;
+import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseIntArray;
 
 import org.junit.runner.RunWith;
 import org.junit.Before;
 import org.junit.Test;
 
+import org.mockito.invocation.InvocationOnMock;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -180,4 +196,73 @@
             assertEquals(deliverToTopWait.who, firstActivity.realActivity);
         }
     }
+
+    @Test
+    public void testApplySleepTokensLocked() throws Exception {
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mock(ActivityStack.class);
+        display.addChild(stack, 0 /* position */);
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(stack);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardShowing(anyInt());
+
+        mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
+        mSupervisor.applySleepTokensLocked(true);
+        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
+
+    @Test
+    public void testTopRunningActivityLockedWithNonExistentDisplay() throws Exception {
+        // Create display that ActivityManagerService does not know about
+        final int unknownDisplayId = 100;
+
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
+            displayIds.put(0, unknownDisplayId);
+            return null;
+        }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
+
+        mSupervisor.mFocusedStack = mock(ActivityStack.class);
+
+        // Supervisor should skip over the non-existent display.
+        assertEquals(null, mSupervisor.topRunningActivityLocked());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 1a95fdd..08158ec 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -98,24 +98,6 @@
     }
 
     @Test
-    public void testNoPauseDuringResumeTopActivity() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-
-        // Simulate the a resumed activity set during
-        // {@link ActivityStack#resumeTopActivityUncheckedLocked}.
-        mSupervisor.inResumeTopActivity = true;
-        r.setState(RESUMED, "testNoPauseDuringResumeTopActivity");
-
-        final boolean waiting = mStack.goToSleepIfPossible(false);
-
-        // Ensure we report not being ready for sleep.
-        assertFalse(waiting);
-
-        // Make sure the resumed activity is untouched.
-        assertEquals(mStack.getResumedActivity(), r);
-    }
-
-    @Test
     public void testResumedActivity() throws Exception {
         final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         assertEquals(mStack.getResumedActivity(), null);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 5906db3..8ff3e45 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -18,13 +18,19 @@
 
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
 import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_SWITCHES_CANCELED;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -45,6 +51,7 @@
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 
 import static org.junit.Assert.assertEquals;
@@ -62,9 +69,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
 
-import static android.app.ActivityManager.START_PERMISSION_DENIED;
-import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
-
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.am.ActivityStarter.Factory;
 import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
@@ -290,7 +294,7 @@
         }
     }
 
-    private ActivityStarter prepareStarter() {
+    private ActivityStarter prepareStarter(int launchFlags) {
         // always allow test to start activity.
         doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
@@ -325,8 +329,20 @@
         // ignore requests to create window container.
         doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean());
 
+
+        final Intent intent = new Intent();
+        intent.addFlags(launchFlags);
+        intent.setComponent(ActivityBuilder.getDefaultComponent());
+
+        final ActivityInfo info = new ActivityInfo();
+
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
+
         return new ActivityStarter(mController, mService,
-                mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
+                .setIntent(intent)
+                .setActivityInfo(info);
     }
 
     /**
@@ -342,9 +358,6 @@
         // add custom values to activity info to make unique.
         final ActivityInfo info = new ActivityInfo();
         final Rect launchBounds = new Rect(0, 0, 20, 30);
-        final Intent intent = new Intent();
-
-        intent.setComponent(ActivityBuilder.getDefaultComponent());
 
         final WindowLayout windowLayout =
                 new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
@@ -354,14 +367,13 @@
         info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
 
         // create starter.
-        final ActivityStarter optionStarter = prepareStarter();
+        final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchBounds(launchBounds);
 
         // run starter.
         optionStarter
-                .setIntent(intent)
                 .setReason("testCreateTaskLayout")
                 .setActivityInfo(info)
                 .setActivityOptions(new SafeActivityOptions(options))
@@ -371,4 +383,69 @@
         verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
                 any(), any());
     }
+
+    /**
+     * This test ensures that if the intent is being delivered to a
+     */
+    @Test
+    public void testSplitScreenDeliverToTop() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Create reusable activity after entering split-screen so that it is the top secondary
+        // stack.
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        // Set focus back to primary.
+        mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
+                focusActivity.getStack());
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(result, START_DELIVERED_TO_TOP);
+    }
+
+    /**
+     * This test ensures that if the intent is being delivered to a split-screen unfocused task
+     * reports it is brought to front instead of delivering to top.
+     */
+    @Test
+    public void testSplitScreenTaskToFront() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        // Create reusable activity here first. Setting the windowing mode of the primary stack
+        // will move the existing standard full screen stack to secondary, putting this one on the
+        // bottom.
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Enter split-screen. Primary stack should have focus.
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenMoveToFront").execute();
+
+        // Ensure result is moving task to front.
+        assertEquals(result, START_TASK_TO_FRONT);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index c130592..6fb1b2e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -353,22 +353,29 @@
      */
     protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
         private ActivityDisplay mDisplay;
+        private KeyguardController mKeyguardController;
 
         public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
             super(service, looper);
             mDisplayManager =
                     (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
             mWindowManager = prepareMockWindowManager();
+            mKeyguardController = mock(KeyguardController.class);
         }
 
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+            mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
             attachDisplay(mDisplay);
         }
 
         @Override
+        public KeyguardController getKeyguardController() {
+            return mKeyguardController;
+        }
+
+        @Override
         ActivityDisplay getDefaultDisplay() {
             return mDisplay;
         }
diff --git a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
index c348e70..d5a28f6 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/AutofillManagerServiceTest.java
@@ -27,8 +27,6 @@
 
 @RunWith(JUnit4.class)
 public class AutofillManagerServiceTest {
-    // TODO(b/74445943): temporary work around until P Development Preview 3 is branched
-    private static final boolean ADDS_DEFAULT_BUTTON = true;
 
     @Test
     public void testGetWhitelistedCompatModePackages_null() {
@@ -42,16 +40,8 @@
 
     @Test
     public void testGetWhitelistedCompatModePackages_onePackageNoUrls() {
-        if (ADDS_DEFAULT_BUTTON) {
-            final Map<String, String[]> result =
-                    getWhitelistedCompatModePackages("one_is_the_loniest_package");
-            assertThat(result).hasSize(1);
-            assertThat(result.get("one_is_the_loniest_package")).asList()
-                    .containsExactly("url_bar", "location_bar_edit_text");
-        } else {
-            assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
-                    .containsExactly("one_is_the_loniest_package", null);
-        }
+        assertThat(getWhitelistedCompatModePackages("one_is_the_loniest_package"))
+                .containsExactly("one_is_the_loniest_package", null);
     }
 
     @Test
@@ -80,12 +70,7 @@
     public void testGetWhitelistedCompatModePackages_multiplePackagesOneInvalid() {
         final Map<String, String[]> result = getWhitelistedCompatModePackages("one:two[");
         assertThat(result).hasSize(1);
-        if (ADDS_DEFAULT_BUTTON) {
-            assertThat(result.get("one")).asList()
-                    .containsExactly("url_bar", "location_bar_edit_text");
-        } else {
-            assertThat(result.get("one")).isNull();
-        }
+        assertThat(result.get("one")).isNull();
     }
 
     @Test
@@ -94,12 +79,7 @@
                 getWhitelistedCompatModePackages("p1[p1u1]:p2:p3[p3u1,p3u2]");
         assertThat(result).hasSize(3);
         assertThat(result.get("p1")).asList().containsExactly("p1u1");
-        if (ADDS_DEFAULT_BUTTON) {
-            assertThat(result.get("p2")).asList()
-                    .containsExactly("url_bar", "location_bar_edit_text");
-        } else {
-            assertThat(result.get("p2")).isNull();
-        }
+        assertThat(result.get("p2")).isNull();
         assertThat(result.get("p3")).asList().containsExactly("p3u1", "p3u2");
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 613d0af..2d5afad 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -32,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
@@ -437,6 +438,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        return null;
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         return null;
     }
@@ -869,8 +875,8 @@
     }
 
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
         return new String[0];
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e8170ee..43490d3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -57,6 +57,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.PasswordMetrics;
+import android.app.backup.ISelectBackupTransportCallback;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -2264,6 +2265,21 @@
         assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
                 intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
 
+        // Make the backup transport selection succeed
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                ISelectBackupTransportCallback callback =
+                    (ISelectBackupTransportCallback) invocation.getArguments()[1];
+                if (callback != null) {
+                    callback.onSuccess("");
+                }
+                return null;
+            }
+        }).when(getServices().ibackupManager).selectBackupTransportAsync(
+                any(ComponentName.class), any(ISelectBackupTransportCallback.class));
+
+
         // Backups are not mandatory
         intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
         assertNull(intent);
@@ -4738,7 +4754,11 @@
 
     public void testOverrideApnAPIsFailWithPO() throws Exception {
         setupProfileOwner();
-        ApnSetting apn = (new ApnSetting.Builder()).build();
+        ApnSetting apn = (new ApnSetting.Builder())
+            .setApnName("test")
+            .setEntryName("test")
+            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
+            .build();
         assertExpectException(SecurityException.class, null, () ->
                 dpm.addOverrideApn(admin1, apn));
         assertExpectException(SecurityException.class, null, () ->
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
index 9213268..e3e61ac 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java
@@ -16,9 +16,8 @@
 
 package com.android.server.devicepolicy;
 
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-
-
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+        .ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
         .OWNER_TRANSFER_METADATA_XML;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.TAG_ADMIN_TYPE;
@@ -32,6 +31,7 @@
 
 import android.os.Environment;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
 
 import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Injector;
 import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Metadata;
@@ -57,6 +57,7 @@
 
 @RunWith(AndroidJUnit4.class)
 public class TransferOwnershipMetadataManagerTest {
+    private final static String TAG = TransferOwnershipMetadataManagerTest.class.getName();
     private final static String SOURCE_COMPONENT =
             "com.dummy.admin.package/com.dummy.admin.package.SourceClassName";
     private final static String TARGET_COMPONENT =
@@ -106,6 +107,22 @@
     @Test
     public void testLoad() {
         TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams();
+        final File transferOwnershipMetadataFile =
+                new File(mMockInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML);
+        Log.d(TAG, "testLoad: file path is " + transferOwnershipMetadataFile.getAbsolutePath());
+        Log.d(TAG, "testLoad: file exists? " + transferOwnershipMetadataFile.exists());
+        Log.d(TAG, "testLoad: file mkdir?" + transferOwnershipMetadataFile.mkdir());
+        try {
+            File canonicalFile = transferOwnershipMetadataFile.getCanonicalFile();
+            File parentFile = canonicalFile.getParentFile();
+            Log.d(TAG, "testLoad: file getCanonicalFile?" + canonicalFile);
+            Log.d(TAG, "testLoad: getCanonicalFile.getParentFile " + parentFile);
+            Log.d(TAG, "testLoad: parent mkdirs? " + parentFile.mkdirs());
+            Log.d(TAG, "testLoad: parent exists? " + parentFile.exists());
+            Log.d(TAG, "testLoad: canonical file.mkdir()? " + canonicalFile.mkdir());
+        } catch (IOException e) {
+            Log.d(TAG, "testLoad: failed to get canonical file");
+        }
         paramsManager.saveMetadataFile(TEST_PARAMS);
         assertEquals(TEST_PARAMS, paramsManager.loadMetadataFile());
     }
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index b4f8474..92cc537 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.display.BrightnessChangeEvent;
@@ -101,20 +102,24 @@
         assertNull(mInjector.mSensorListener);
         assertNotNull(mInjector.mBroadcastReceiver);
         assertTrue(mInjector.mIdleScheduled);
-        Intent onIntent = new Intent();
-        onIntent.setAction(Intent.ACTION_SCREEN_ON);
-        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
-                onIntent);
+        mInjector.sendScreenChange(/*screen on */ true);
         assertNotNull(mInjector.mSensorListener);
 
-        Intent offIntent = new Intent();
-        offIntent.setAction(Intent.ACTION_SCREEN_OFF);
-        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
-                offIntent);
+        mInjector.sendScreenChange(/*screen on */ false);
         assertNull(mInjector.mSensorListener);
 
-        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
-                onIntent);
+        // Turn screen on while brightness mode is manual
+        mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ false);
+        mInjector.sendScreenChange(/*screen on */ true);
+        assertNull(mInjector.mSensorListener);
+
+        // Set brightness mode to automatic while screen is off.
+        mInjector.sendScreenChange(/*screen on */ false);
+        mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ true);
+        assertNull(mInjector.mSensorListener);
+
+        // Turn on screen while brightness mode is automatic.
+        mInjector.sendScreenChange(/*screen on */ true);
         assertNotNull(mInjector.mSensorListener);
 
         mTracker.stop();
@@ -124,6 +129,37 @@
     }
 
     @Test
+    public void testAdaptiveOnOff() {
+        mInjector.mInteractive = true;
+        mInjector.mIsBrightnessModeAutomatic = false;
+        startTracker(mTracker);
+        assertNull(mInjector.mSensorListener);
+        assertNotNull(mInjector.mBroadcastReceiver);
+        assertNotNull(mInjector.mContentObserver);
+        assertTrue(mInjector.mIdleScheduled);
+
+        mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
+        assertNotNull(mInjector.mSensorListener);
+
+        SensorEventListener listener = mInjector.mSensorListener;
+        mInjector.mSensorListener = null;
+        // Duplicate notification
+        mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
+        // Sensor shouldn't have been registered as it was already registered.
+        assertNull(mInjector.mSensorListener);
+        mInjector.mSensorListener = listener;
+
+        mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false);
+        assertNull(mInjector.mSensorListener);
+
+        mTracker.stop();
+        assertNull(mInjector.mSensorListener);
+        assertNull(mInjector.mBroadcastReceiver);
+        assertNull(mInjector.mContentObserver);
+        assertFalse(mInjector.mIdleScheduled);
+    }
+
+    @Test
     public void testBrightnessEvent() {
         final int brightness = 20;
 
@@ -618,16 +654,39 @@
         boolean mIdleScheduled;
         boolean mInteractive = true;
         int[] mProfiles;
+        ContentObserver mContentObserver;
+        boolean mIsBrightnessModeAutomatic = true;
 
         public TestInjector(Handler handler) {
             mHandler = handler;
         }
 
-        public void incrementTime(long timeMillis) {
+        void incrementTime(long timeMillis) {
             mCurrentTimeMillis += timeMillis;
             mElapsedRealtimeNanos += TimeUnit.MILLISECONDS.toNanos(timeMillis);
         }
 
+        void setBrightnessMode(boolean isBrightnessModeAutomatic) {
+          mIsBrightnessModeAutomatic = isBrightnessModeAutomatic;
+          mContentObserver.dispatchChange(false, null);
+          waitForHandler();
+        }
+
+        void sendScreenChange(boolean screenOn) {
+            mInteractive = screenOn;
+            Intent intent = new Intent();
+            intent.setAction(screenOn ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
+            mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), intent);
+            waitForHandler();
+        }
+
+        void waitForHandler() {
+            Idle idle = new Idle();
+            mHandler.getLooper().getQueue().addIdleHandler(idle);
+            mHandler.post(() -> {});
+            idle.waitForIdle();
+        }
+
         @Override
         public void registerSensorListener(Context context,
                 SensorEventListener sensorListener, Handler handler) {
@@ -641,6 +700,18 @@
         }
 
         @Override
+        public void registerBrightnessModeObserver(ContentResolver resolver,
+                ContentObserver settingsObserver) {
+            mContentObserver = settingsObserver;
+        }
+
+        @Override
+        public void unregisterBrightnessModeObserver(Context context,
+                ContentObserver settingsObserver) {
+            mContentObserver = null;
+        }
+
+        @Override
         public void registerReceiver(Context context,
                 BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter) {
             mBroadcastReceiver = shutdownReceiver;
@@ -658,11 +729,9 @@
             return mHandler;
         }
 
-        public void waitForHandler() {
-            Idle idle = new Idle();
-            mHandler.getLooper().getQueue().addIdleHandler(idle);
-            mHandler.post(() -> {});
-            idle.waitForIdle();
+        @Override
+        public boolean isBrightnessModeAutomatic(ContentResolver resolver) {
+            return mIsBrightnessModeAutomatic;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 69796b3..0ea2317 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -62,7 +62,6 @@
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
@@ -95,7 +94,6 @@
     private RecoverySnapshotStorage mRecoverySnapshotStorage;
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
-    private KeyPair mKeyPair;
     private AndroidKeyStoreSecretKey mWrappingKey;
     private PlatformEncryptionKey mEncryptKey;
 
@@ -108,7 +106,6 @@
         Context context = InstrumentationRegistry.getTargetContext();
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
-        mKeyPair = SecureBox.genKeyPair();
 
         mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
                 new int[] {TYPE_LOCKSCREEN});
@@ -249,8 +246,8 @@
                 TEST_RECOVERY_AGENT_UID,
                 TEST_APP_KEY_ALIAS,
                 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
 
         mKeySyncTask.run();
@@ -265,8 +262,8 @@
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
         mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
 
         mKeySyncTask.run();
 
@@ -275,8 +272,8 @@
 
     @Test
     public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
 
         mRecoverableKeyStoreDb.setServerParams(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
@@ -296,13 +293,12 @@
                 keyDerivationParams.getSalt(),
                 TEST_CREDENTIAL);
         Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
-        counterId = 1L; // TODO: use value from the database.
         assertThat(counterId).isNotNull();
         byte[] recoveryKey = decryptThmEncryptedKey(
                 lockScreenHash,
                 keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
                 /*vaultParams=*/ KeySyncUtils.packVaultParams(
-                        mKeyPair.getPublic(),
+                        TestData.CERT_1_PUBLIC_KEY,
                         counterId,
                         /*maxAttempts=*/ 10,
                         TEST_VAULT_HANDLE));
@@ -310,8 +306,8 @@
         assertThat(applicationKeys).hasSize(1);
         assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
         assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
-        assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
-                .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic()));
+        assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+                .isEqualTo(TestData.CERT_PATH_1);
         assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
         WrappedApplicationKey keyData = applicationKeys.get(0);
         assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
@@ -336,15 +332,14 @@
         verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
         List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
         assertThat(applicationKeys).hasSize(1);
-        assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
-                .isEqualTo(SecureBox.encodePublicKey(
-                        TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
+        assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
+                .isEqualTo(TestData.CERT_PATH_1);
     }
 
     @Test
     public void run_setsCorrectSnapshotVersion() throws Exception {
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
@@ -362,8 +357,8 @@
 
     @Test
     public void run_recreatesMissingSnapshot() throws Exception {
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
@@ -392,8 +387,8 @@
                 /*credentialUpdated=*/ false,
                 mPlatformKeyManager);
 
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -418,8 +413,8 @@
                 /*credentialUpdated=*/ false,
                 mPlatformKeyManager);
 
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -445,8 +440,8 @@
                 /*credentialUpdated=*/ false,
                 mPlatformKeyManager);
 
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -461,10 +456,10 @@
 
     @Test
     public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -483,10 +478,10 @@
         mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
                 new int[] {1000});
 
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -500,10 +495,10 @@
 
     @Test
     public void run_notifiesNonregisteredAgent() throws Exception {
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
-        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -563,7 +558,7 @@
     private byte[] decryptThmEncryptedKey(
             byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
         byte[] locallyEncryptedKey = SecureBox.decrypt(
-                mKeyPair.getPrivate(),
+                TestData.CERT_1_PRIVATE_KEY,
                 /*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
                 /*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
                 encryptedKey
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index a251c9d..fae48c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -52,6 +52,8 @@
     private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
     private static final byte[] TEST_VAULT_HANDLE =
             new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+    private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
+    private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
     private static final String SHA_256_ALGORITHM = "SHA-256";
     private static final String APPLICATION_KEY_ALGORITHM = "AES";
     private static final byte[] LOCK_SCREEN_HASH_1 =
@@ -63,8 +65,7 @@
     private static final byte[] RECOVERY_RESPONSE_HEADER =
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
     private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
-    private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
-    private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
+
 
     @Test
     public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -345,7 +346,7 @@
     }
 
     @Test
-    public void packVaultParams_returns94Bytes() throws Exception {
+    public void packVaultParams_returnsCorrectSize() throws Exception {
         PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
 
         byte[] packedForm = KeySyncUtils.packVaultParams(
@@ -420,6 +421,24 @@
         assertArrayEquals(TEST_VAULT_HANDLE, vaultHandle);
     }
 
+    @Test
+    public void packVaultParams_encodesVaultHandleWithLength8AsLastParam() throws Exception {
+        byte[] vaultHandleWithLenght8 = new byte[] {1, 2, 3, 4, 1, 2, 3, 4};
+        byte[] packedForm = KeySyncUtils.packVaultParams(
+                SecureBox.genKeyPair().getPublic(),
+                /*counterId=*/ 10021L,
+                /*maxAttempts=*/ 10,
+                vaultHandleWithLenght8);
+
+        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
+                .order(ByteOrder.LITTLE_ENDIAN);
+        assertEquals(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES + 8, packedForm.length);
+        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
+        byte[] vaultHandle = new byte[8];
+        byteBuffer.get(vaultHandle);
+        assertArrayEquals(vaultHandleWithLenght8, vaultHandle);
+    }
+
     private static byte[] randomBytes(int n) {
         byte[] bytes = new byte[n];
         new Random().nextBytes(bytes);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 0ceb558..0d6d525b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -40,6 +40,7 @@
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
@@ -68,6 +69,7 @@
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -76,8 +78,10 @@
 import java.util.Map;
 import java.util.Random;
 
+import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 @SmallTest
@@ -85,7 +89,7 @@
 public class RecoverableKeyStoreManagerTest {
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
 
-    private static final String ROOT_CERTIFICATE_ALIAS = "put_default_alias_here";
+    private static final String ROOT_CERTIFICATE_ALIAS = "";
     private static final String TEST_SESSION_ID = "karlin";
     private static final byte[] TEST_PUBLIC_KEY = new byte[] {
         (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -139,12 +143,12 @@
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
+    private static final String TEST_ROOT_CERT_ALIAS = "";
 
     @Mock private Context mMockContext;
     @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
     @Mock private KeyguardManager mKeyguardManager;
     @Mock private PlatformKeyManager mPlatformKeyManager;
-    @Mock private KeyStore mKeyStore;
     @Mock private ApplicationKeyStorage mApplicationKeyStorage;
 
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -175,7 +179,7 @@
 
         mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
                 mMockContext,
-                mKeyStore,
+                KeyStore.getInstance(),
                 mRecoverableKeyStoreDb,
                 mRecoverySessionStorage,
                 Executors.newSingleThreadExecutor(),
@@ -219,6 +223,7 @@
 
         assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
         assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
@@ -237,8 +242,8 @@
         try {
             mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
             fail("should have thrown");
-        } catch (ServiceSpecificException e) {
-            assertThat(e.getMessage()).contains("not contain 256 bits");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("is null");
         }
     }
 
@@ -295,6 +300,23 @@
     }
 
     @Test
+    public void initRecoveryService_regeneratesCounterId() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        long certSerial = 1000L;
+
+        Long counterId0 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+        mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+                TestData.getCertXmlWithSerial(certSerial));
+        Long counterId1 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+        mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+                TestData.getCertXmlWithSerial(certSerial + 1));
+        Long counterId2 = mRecoverableKeyStoreDb.getCounterId(userId, uid);
+
+        assertThat(!counterId1.equals(counterId0) || !counterId2.equals(counterId1)).isTrue();
+    }
+
+    @Test
     public void initRecoveryService_throwsIfInvalidCert() throws Exception {
         byte[] modifiedCertXml = TestData.getCertXml();
         modifiedCertXml[modifiedCertXml.length - 50] ^= 1;  // Flip a bit in the certificate
@@ -388,7 +410,7 @@
                     ROOT_CERTIFICATE_ALIAS, /*recoveryServiceCertFile=*/ null,
                     TestData.getSigXml());
             fail("should have thrown");
-        } catch (ServiceSpecificException e) {
+        } catch (NullPointerException e) {
             assertThat(e.getMessage()).contains("is null");
         }
     }
@@ -400,7 +422,7 @@
                     ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(),
                     /*recoveryServiceSigFile=*/ null);
             fail("should have thrown");
-        } catch (ServiceSpecificException e) {
+        } catch (NullPointerException e) {
             assertThat(e.getMessage()).contains("is null");
         }
     }
@@ -449,10 +471,13 @@
                         eq(Manifest.permission.RECOVER_KEYSTORE), any());
     }
 
+    // TODO: Add tests for non-existing cert alias
+
     @Test
     public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
+                TEST_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -474,6 +499,7 @@
     public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
+                TEST_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -549,6 +575,16 @@
     }
 
     @Test
+    public void closeSession_throwsIfNullSession() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.closeSession(/*sessionId=*/ null);
+            fail("should have thrown");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("invalid");
+        }
+    }
+
+    @Test
     public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
         try {
             mRecoverableKeyStoreManager.startRecoverySession(
@@ -591,6 +627,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -609,6 +646,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     vaultParams,
                     TEST_VAULT_CHALLENGE,
@@ -631,6 +669,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -655,6 +694,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
+                    TEST_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(shortCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -671,9 +711,9 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception {
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*recoveryKeyBlob=*/ randomBytes(32),
                     /*applicationKeys=*/ ImmutableList.of(
@@ -686,7 +726,7 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -699,7 +739,7 @@
                         TEST_SECRET)));
 
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ randomBytes(60),
                     /*applicationKeys=*/ ImmutableList.of());
@@ -710,7 +750,7 @@
     }
 
     @Test
-    public void recoverKeys_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -731,7 +771,7 @@
                 encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)));
 
         try {
-            mRecoverableKeyStoreManager.recoverKeys(
+            mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ encryptedClaimResponse,
                     /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
@@ -742,7 +782,8 @@
     }
 
     @Test
-    public void recoverKeys_doesNotThrowIfNoApplicationKeysToBeDecrypted() throws Exception {
+    public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted()
+            throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -759,14 +800,14 @@
         byte[] encryptedClaimResponse = encryptClaimResponse(
                 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
 
-        mRecoverableKeyStoreManager.recoverKeys(
+        mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 /*encryptedRecoveryKey=*/ encryptedClaimResponse,
                 /*applicationKeys=*/ ImmutableList.of());
     }
 
     @Test
-    public void recoverKeys_returnsDecryptedKeys() throws Exception {
+    public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -787,17 +828,18 @@
                 TEST_ALIAS,
                 encryptedApplicationKey(recoveryKey, applicationKeyBytes));
 
-        Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
+        Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 encryptedClaimResponse,
                 ImmutableList.of(applicationKey));
 
         assertThat(recoveredKeys).hasSize(1);
-        assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes);
+        assertThat(recoveredKeys).containsKey(TEST_ALIAS);
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
-    public void recoverKeys_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
+    public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -825,13 +867,14 @@
                 TEST_ALIAS2,
                 encryptedApplicationKey(recoveryKey, applicationKeyBytes2));
 
-        Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
+        Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                 TEST_SESSION_ID,
                 encryptedClaimResponse,
                 ImmutableList.of(applicationKey1, applicationKey2));
 
         assertThat(recoveredKeys).hasSize(1);
-        assertThat(recoveredKeys.get(TEST_ALIAS2)).isEqualTo(applicationKeyBytes2);
+        assertThat(recoveredKeys).containsKey(TEST_ALIAS2);
+        // TODO(76083050) Test the grant mechanism for the keys.
     }
 
     @Test
@@ -864,6 +907,16 @@
     }
 
     @Test
+    public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
+            fail("should have thrown");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("is null");
+        }
+    }
+
+    @Test
     public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
@@ -897,6 +950,16 @@
         assertThat(statuses).containsEntry(alias, status2); // updated
     }
 
+    @Test
+    public void setRecoveryStatus_throwsIfNullAlias() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.setRecoveryStatus(/*alias=*/ null, /*status=*/ 100);
+            fail("should have thrown");
+        } catch (NullPointerException e) {
+            assertThat(e.getMessage()).contains("is null");
+        }
+    }
+
     private static byte[] encryptedApplicationKey(
             SecretKey recoveryKey, byte[] applicationKey) throws Exception {
         return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index b5d6ce8..4b059c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -1,11 +1,18 @@
 package com.android.server.locksettings.recoverablekeystore;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
 
 import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
-import java.security.cert.CertPath;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
+import java.security.spec.ECPrivateKeySpec;
 
 public final class TestData {
 
@@ -213,6 +220,44 @@
             + "  </value>\n"
             + "</signature>\n";
 
+    public static final PublicKey CERT_1_PUBLIC_KEY;
+    public static final PrivateKey CERT_1_PRIVATE_KEY;
+
+    static {
+        try {
+            CERT_1_PUBLIC_KEY =
+                    SecureBox.decodePublicKey(
+                            new byte[] {
+                                (byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18,
+                                (byte) 0x98, (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4,
+                                (byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda, (byte) 0x1c,
+                                (byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a,
+                                (byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0,
+                                (byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
+                                (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
+                                (byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79,
+                                (byte) 0x20, (byte) 0x1d, (byte) 0x91, (byte) 0x55, (byte) 0xb0,
+                                (byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b, (byte) 0x32,
+                                (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc,
+                                (byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
+                                (byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa
+                            });
+            CERT_1_PRIVATE_KEY =
+                    decodePrivateKey(
+                            new byte[] {
+                                (byte) 0x70, (byte) 0x01, (byte) 0xc7, (byte) 0x87, (byte) 0x32,
+                                (byte) 0x2f, (byte) 0x1c, (byte) 0x9a, (byte) 0x6e, (byte) 0xb1,
+                                (byte) 0x91, (byte) 0xca, (byte) 0x4e, (byte) 0xb5, (byte) 0x44,
+                                (byte) 0xba, (byte) 0xc8, (byte) 0x68, (byte) 0xc6, (byte) 0x0a,
+                                (byte) 0x76, (byte) 0xcb, (byte) 0xd3, (byte) 0x63, (byte) 0x67,
+                                (byte) 0x7c, (byte) 0xb0, (byte) 0x11, (byte) 0x82, (byte) 0x65,
+                                (byte) 0x77, (byte) 0x01
+                            });
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
     public static byte[] getCertPath1Bytes() {
         try {
             return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
@@ -256,4 +301,11 @@
     public static byte[] getSigXml() {
         return THM_SIG_XML.getBytes(StandardCharsets.UTF_8);
     }
+
+    private static PrivateKey decodePrivateKey(byte[] keyBytes) throws Exception {
+        assertThat(keyBytes.length).isEqualTo(32);
+        BigInteger priv = new BigInteger(/*signum=*/ 1, keyBytes);
+        KeyFactory keyFactory = KeyFactory.getInstance("EC");
+        return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
deleted file mode 100644
index 180345c..0000000
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/PersistentKeyChainSnapshotTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.locksettings.recoverablekeystore.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
-import android.security.keystore.recovery.KeyDerivationParams;
-import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.keystore.recovery.KeyChainSnapshot;
-import android.security.keystore.recovery.KeyChainProtectionParams;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class PersistentKeyChainSnapshotTest {
-
-    private static final String ALIAS = "some_key";
-    private static final String ALIAS2 = "another_key";
-    private static final byte[] RECOVERY_KEY_MATERIAL = "recovery_key_data"
-            .getBytes(StandardCharsets.UTF_8);
-    private static final byte[] KEY_MATERIAL = "app_key_data".getBytes(StandardCharsets.UTF_8);
-    private static final byte[] PUBLIC_KEY = "public_key_data".getBytes(StandardCharsets.UTF_8);
-    private static final byte[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
-    private static final int SNAPSHOT_VERSION = 2;
-    private static final int MAX_ATTEMPTS = 10;
-    private static final long COUNTER_ID = 123456789L;
-    private static final byte[] SERVER_PARAMS = "server_params".getBytes(StandardCharsets.UTF_8);
-    private static final byte[] ZERO_BYTES = new byte[0];
-    private static final byte[] ONE_BYTE = new byte[]{(byte) 11};
-    private static final byte[] TWO_BYTES = new byte[]{(byte) 222,(byte) 222};
-
-    @Test
-    public void testWriteInt() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        writer.writeInt(Integer.MIN_VALUE);
-        writer.writeInt(Integer.MAX_VALUE);
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-        assertThat(reader.readInt()).isEqualTo(Integer.MIN_VALUE);
-        assertThat(reader.readInt()).isEqualTo(Integer.MAX_VALUE);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readInt());
-    }
-
-    @Test
-    public void testWriteLong() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        writer.writeLong(Long.MIN_VALUE);
-        writer.writeLong(Long.MAX_VALUE);
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-        assertThat(reader.readLong()).isEqualTo(Long.MIN_VALUE);
-        assertThat(reader.readLong()).isEqualTo(Long.MAX_VALUE);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readLong());
-    }
-
-    @Test
-    public void testWriteBytes() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        writer.writeBytes(ZERO_BYTES);
-        writer.writeBytes(ONE_BYTE);
-        writer.writeBytes(TWO_BYTES);
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-        assertThat(reader.readBytes()).isEqualTo(ZERO_BYTES);
-        assertThat(reader.readBytes()).isEqualTo(ONE_BYTE);
-        assertThat(reader.readBytes()).isEqualTo(TWO_BYTES);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readBytes());
-    }
-
-    @Test
-    public void testReadBytes_returnsNullArrayAsEmpty() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        writer.writeBytes(null);
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-        assertThat(reader.readBytes()).isEqualTo(new byte[]{}); // null -> empty array
-    }
-
-    @Test
-    public void testWriteKeyEntry() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
-                .setAlias(ALIAS)
-                .setEncryptedKeyMaterial(KEY_MATERIAL)
-                .build();
-        writer.writeKeyEntry(entry);
-
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-
-        WrappedApplicationKey copy = reader.readKeyEntry();
-        assertThat(copy.getAlias()).isEqualTo(ALIAS);
-        assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readKeyEntry());
-    }
-
-    @Test
-    public void testWriteProtectionParams() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-        KeyChainProtectionParams protectionParams =  new KeyChainProtectionParams.Builder()
-                .setUserSecretType(1)
-                .setLockScreenUiFormat(2)
-                .setKeyDerivationParams(derivationParams)
-                .build();
-        writer.writeProtectionParams(protectionParams);
-
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-
-        KeyChainProtectionParams copy = reader.readProtectionParams();
-        assertThat(copy.getUserSecretType()).isEqualTo(1);
-        assertThat(copy.getLockScreenUiFormat()).isEqualTo(2);
-        assertThat(copy.getKeyDerivationParams().getSalt()).isEqualTo(SALT);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readProtectionParams());
-    }
-
-    @Test
-    public void testKeyChainSnapshot() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-
-        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
-        ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
-        protectionParamsList.add(new KeyChainProtectionParams.Builder()
-                .setUserSecretType(1)
-                .setLockScreenUiFormat(2)
-                .setKeyDerivationParams(derivationParams)
-                .build());
-
-        ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
-        appKeysList.add(new WrappedApplicationKey.Builder()
-                .setAlias(ALIAS)
-                .setEncryptedKeyMaterial(KEY_MATERIAL)
-                .build());
-
-        KeyChainSnapshot snapshot =  new KeyChainSnapshot.Builder()
-                .setSnapshotVersion(SNAPSHOT_VERSION)
-                .setKeyChainProtectionParams(protectionParamsList)
-                .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
-                .setWrappedApplicationKeys(appKeysList)
-                .setMaxAttempts(MAX_ATTEMPTS)
-                .setCounterId(COUNTER_ID)
-                .setServerParams(SERVER_PARAMS)
-                .setTrustedHardwarePublicKey(PUBLIC_KEY)
-                .build();
-
-        writer.writeKeyChainSnapshot(snapshot);
-
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-
-        KeyChainSnapshot copy = reader.readKeyChainSnapshot();
-        assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
-        assertThat(copy.getKeyChainProtectionParams()).hasSize(1);
-        assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
-        assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
-        assertThat(copy.getWrappedApplicationKeys()).hasSize(1);
-        assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
-        assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
-        assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
-        assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
-        assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readKeyChainSnapshot());
-
-        verifyDeserialize(snapshot);
-    }
-
-    @Test
-    public void testKeyChainSnapshot_withManyKeysAndProtectionParams() throws Exception {
-        PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
-        writer.initWriter();
-
-        KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
-
-        ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
-        protectionParamsList.add(new KeyChainProtectionParams.Builder()
-                .setUserSecretType(1)
-                .setLockScreenUiFormat(2)
-                .setKeyDerivationParams(derivationParams)
-                .build());
-        protectionParamsList.add(new KeyChainProtectionParams.Builder()
-                .setUserSecretType(2)
-                .setLockScreenUiFormat(3)
-                .setKeyDerivationParams(derivationParams)
-                .build());
-        ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
-        appKeysList.add(new WrappedApplicationKey.Builder()
-                .setAlias(ALIAS)
-                .setEncryptedKeyMaterial(KEY_MATERIAL)
-                .build());
-        appKeysList.add(new WrappedApplicationKey.Builder()
-                .setAlias(ALIAS2)
-                .setEncryptedKeyMaterial(KEY_MATERIAL)
-                .build());
-
-
-        KeyChainSnapshot snapshot =  new KeyChainSnapshot.Builder()
-                .setSnapshotVersion(SNAPSHOT_VERSION)
-                .setKeyChainProtectionParams(protectionParamsList)
-                .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
-                .setWrappedApplicationKeys(appKeysList)
-                .setMaxAttempts(MAX_ATTEMPTS)
-                .setCounterId(COUNTER_ID)
-                .setServerParams(SERVER_PARAMS)
-                .setTrustedHardwarePublicKey(PUBLIC_KEY)
-                .build();
-
-        writer.writeKeyChainSnapshot(snapshot);
-
-        byte[] result = writer.getOutput();
-
-        PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
-        reader.initReader(result);
-
-        KeyChainSnapshot copy = reader.readKeyChainSnapshot();
-        assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
-        assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
-        assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
-        assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
-        assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
-        assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
-        assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
-        assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
-
-        assertThrows(
-                IOException.class,
-                () -> reader.readKeyChainSnapshot());
-
-        verifyDeserialize(snapshot);
-    }
-
-    private void verifyDeserialize(KeyChainSnapshot snapshot) throws Exception {
-        byte[] serialized = PersistentKeyChainSnapshot.serialize(snapshot);
-        KeyChainSnapshot copy = PersistentKeyChainSnapshot.deserialize(serialized);
-        assertThat(copy.getSnapshotVersion())
-                .isEqualTo(snapshot.getSnapshotVersion());
-        assertThat(copy.getKeyChainProtectionParams().size())
-                .isEqualTo(copy.getKeyChainProtectionParams().size());
-        assertThat(copy.getEncryptedRecoveryKeyBlob())
-                .isEqualTo(snapshot.getEncryptedRecoveryKeyBlob());
-        assertThat(copy.getWrappedApplicationKeys().size())
-                .isEqualTo(snapshot.getWrappedApplicationKeys().size());
-        assertThat(copy.getMaxAttempts()).isEqualTo(snapshot.getMaxAttempts());
-        assertThat(copy.getCounterId()).isEqualTo(snapshot.getCounterId());
-        assertThat(copy.getServerParams()).isEqualTo(snapshot.getServerParams());
-        assertThat(copy.getTrustedHardwarePublicKey())
-                .isEqualTo(snapshot.getTrustedHardwarePublicKey());
-    }
-
-    @Test
-    public void testDeserialize_failsForNewerVersion() throws Exception {
-        byte[] newVersion = new byte[]{(byte) 2, (byte) 0, (byte) 0, (byte) 0};
-        assertThrows(
-                IOException.class,
-                () -> PersistentKeyChainSnapshot.deserialize(newVersion));
-    }
-
-    @Test
-    public void testDeserialize_failsForEmptyData() throws Exception {
-        byte[] empty = new byte[]{};
-        assertThrows(
-                IOException.class,
-                () -> PersistentKeyChainSnapshot.deserialize(empty));
-    }
-
-}
-
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 999dce5..6f2237f 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
@@ -75,11 +75,11 @@
         Map<String, Boolean> result = PrivacyUtils.createDpEncodedReportMap(false, null,
                 TEST_DIGEST_LIST, TEST_AGGREGATED_RESULT1);
         assertEquals(6, result.size());
-        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
+        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
-        assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
-        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+        assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
         assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 53d97e7..ebb4248 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -21,13 +21,12 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -39,12 +38,13 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.UserInfo;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
-import android.security.keystore.ArrayUtils;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -54,8 +54,8 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,9 +71,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PackageManagerSettingsTests {
-    private static final String PACKAGE_NAME_2 = "com.google.app2";
+    private static final String PACKAGE_NAME_2 = "com.android.app2";
     private static final String PACKAGE_NAME_3 = "com.android.app3";
-    private static final String PACKAGE_NAME_1 = "com.google.app1";
+    private static final String PACKAGE_NAME_1 = "com.android.app1";
     public static final String TAG = "PackageManagerSettingsTests";
     protected final String PREFIX = "android.content.pm";
 
@@ -156,6 +156,92 @@
         assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT));
     }
 
+    private PersistableBundle getPersistableBundle(String packageName, long longVal,
+            double doubleVal, boolean boolVal, String textVal) {
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(packageName + ".TEXT_VALUE", textVal);
+        bundle.putLong(packageName + ".LONG_VALUE", longVal);
+        bundle.putBoolean(packageName + ".BOOL_VALUE", boolVal);
+        bundle.putDouble(packageName + ".DOUBLE_VALUE", doubleVal);
+        return bundle;
+    }
+
+    @Test
+    public void testReadPackageRestrictions_oldSuspendInfo() {
+        writePackageRestrictions_oldSuspendInfoXml(0);
+        final Object lock = new Object();
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+        settingsUnderTest.readPackageRestrictionsLPr(0);
+
+        final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
+        final PackageUserState packageUserState1 = ps1.readUserState(0);
+        assertThat(packageUserState1.suspended, is(true));
+        assertThat("android".equals(packageUserState1.suspendingPackage), is(true));
+
+        final PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
+        final PackageUserState packageUserState2 = ps2.readUserState(0);
+        assertThat(packageUserState2.suspended, is(false));
+        assertThat(packageUserState2.suspendingPackage, is(nullValue()));
+    }
+
+    @Test
+    public void testReadWritePackageRestrictions_newSuspendInfo() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+        final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
+
+        final PersistableBundle appExtras1 = getPersistableBundle(
+                PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
+        final PersistableBundle launcherExtras1 = getPersistableBundle(
+                PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
+        ps1.setSuspended(true, "suspendingPackage1", appExtras1, launcherExtras1, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+
+        ps2.setSuspended(true, "suspendingPackage2", null, null, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+        ps3.setSuspended(false, "irrelevant", null, null, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
+
+        settingsUnderTest.writePackageRestrictionsLPr(0);
+
+        settingsUnderTest.mPackages.clear();
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
+        // now read and verify
+        settingsUnderTest.readPackageRestrictionsLPr(0);
+        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1).
+                readUserState(0);
+        assertThat(readPus1.suspended, is(true));
+        assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
+        assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
+        assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
+                is(true));
+
+        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2).
+                readUserState(0);
+        assertThat(readPus2.suspended, is(true));
+        assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
+        assertThat(readPus2.suspendedAppExtras, is(nullValue()));
+        assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
+
+        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
+                readUserState(0);
+        assertThat(readPus3.suspended, is(false));
+    }
+
+    @Test
+    public void testPackageRestrictionsSuspendedDefault() {
+        final PackageSetting defaultSetting =  createPackageSetting(PACKAGE_NAME_1);
+        assertThat(defaultSetting.getSuspended(0), is(false));
+    }
+
     @Test
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
@@ -686,6 +772,26 @@
                 null /*usesStaticLibrariesVersions*/);
     }
 
+    private PackageSetting createPackageSetting(String packageName) {
+        return new PackageSetting(
+                packageName,
+                packageName,
+                INITIAL_CODE_PATH /*codePath*/,
+                INITIAL_CODE_PATH /*resourcePath*/,
+                null /*legacyNativeLibraryPathString*/,
+                "x86_64" /*primaryCpuAbiString*/,
+                "x86" /*secondaryCpuAbiString*/,
+                null /*cpuAbiOverrideString*/,
+                INITIAL_VERSION_CODE,
+                0,
+                0 /*privateFlags*/,
+                null /*parentPackageName*/,
+                null /*childPackageNames*/,
+                0,
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
+    }
+
     private @NonNull List<UserInfo> createFakeUsers() {
         ArrayList<UserInfo> users = new ArrayList<>();
         users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
@@ -718,13 +824,13 @@
                 + "<item name=\"android.permission.ACCESS_WIMAX_STATE\" package=\"android\" />"
                 + "<item name=\"android.permission.REBOOT\" package=\"android\" protection=\"18\" />"
                 + "</permissions>"
-                + "<package name=\"com.google.app1\" codePath=\"/system/app/app1.apk\" nativeLibraryPath=\"/data/data/com.google.app1/lib\" flags=\"1\" ft=\"1360e2caa70\" it=\"135f2f80d08\" ut=\"1360e2caa70\" version=\"1109\" sharedUserId=\"11000\">"
+                + "<package name=\"com.android.app1\" codePath=\"/system/app/app1.apk\" nativeLibraryPath=\"/data/data/com.android.app1/lib\" flags=\"1\" ft=\"1360e2caa70\" it=\"135f2f80d08\" ut=\"1360e2caa70\" version=\"1109\" sharedUserId=\"11000\">"
                 + "<sigs count=\"1\">"
                 + "<cert index=\"0\" key=\"" + KeySetStrings.ctsKeySetCertA + "\" />"
                 + "</sigs>"
                 + "<proper-signing-keyset identifier=\"1\" />"
                 + "</package>"
-                + "<package name=\"com.google.app2\" codePath=\"/system/app/app2.apk\" nativeLibraryPath=\"/data/data/com.google.app2/lib\" flags=\"1\" ft=\"1360e578718\" it=\"135f2f80d08\" ut=\"1360e578718\" version=\"15\" enabled=\"3\" userId=\"11001\">"
+                + "<package name=\"com.android.app2\" codePath=\"/system/app/app2.apk\" nativeLibraryPath=\"/data/data/com.android.app2/lib\" flags=\"1\" ft=\"1360e578718\" it=\"135f2f80d08\" ut=\"1360e578718\" version=\"15\" enabled=\"3\" userId=\"11001\">"
                 + "<sigs count=\"1\">"
                 + "<cert index=\"0\" />"
                 + "</sigs>"
@@ -774,11 +880,26 @@
                 + "</packages>").getBytes());
     }
 
+    private void writePackageRestrictions_oldSuspendInfoXml(final int userId) {
+        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/"
+                        + userId + "/package-restrictions.xml"),
+                ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                        + "<package-restrictions>\n"
+                        + "    <pkg name=\"" + PACKAGE_NAME_1 + "\" suspended=\"true\" />"
+                        + "    <pkg name=\"" + PACKAGE_NAME_2 + "\" suspended=\"false\" />"
+                        + "    <preferred-activities />\n"
+                        + "    <persistent-preferred-activities />\n"
+                        + "    <crossProfile-intent-filters />\n"
+                        + "    <default-apps />\n"
+                        + "</package-restrictions>\n")
+                        .getBytes());
+    }
+
     private void writeStoppedPackagesXml() {
         writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
                 ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                 + "<stopped-packages>"
-                + "<pkg name=\"com.google.app1\" nl=\"1\" />"
+                + "<pkg name=\"com.android.app1\" nl=\"1\" />"
                 + "<pkg name=\"com.android.app3\" nl=\"1\" />"
                 + "</stopped-packages>")
                 .getBytes());
@@ -786,8 +907,8 @@
 
     private void writePackagesList() {
         writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.list"),
-                ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
-                + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
+                ( "com.android.app1 11000 0 /data/data/com.android.app1 seinfo1"
+                + "com.android.app2 11001 0 /data/data/com.android.app2 seinfo2"
                 + "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
                 .getBytes());
     }
@@ -828,6 +949,11 @@
         });
     }
 
+    @After
+    public void tearDown() throws Exception {
+        deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
+    }
+
     private void verifyKeySetMetaData(Settings settings)
             throws ReflectiveOperationException, IllegalAccessException {
         ArrayMap<String, PackageSetting> packages = settings.mPackages;
@@ -871,9 +997,9 @@
         assertThat(KeySetUtils.getLastIssuedKeySetId(ksms), is(4L));
 
         /* verify packages have been given the appropriate information */
-        PackageSetting ps = packages.get("com.google.app1");
+        PackageSetting ps = packages.get("com.android.app1");
         assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
-        ps = packages.get("com.google.app2");
+        ps = packages.get("com.android.app2");
         assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
         assertThat(ps.keySetData.getAliases().get("AB"), is(4L));
         ps = packages.get("com.android.app3");
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index c491601..2f6e2c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -27,8 +27,10 @@
 import android.content.pm.Signature;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import java.io.File;
 import java.lang.reflect.Array;
@@ -126,6 +128,8 @@
     }
 
     @Test
+    @SmallTest
+    @Presubmit
     public void test_roundTripKnownFields() throws Exception {
         PackageParser.Package pkg = new PackageParser.Package("foo");
         setKnownFields(pkg);
@@ -266,6 +270,9 @@
         assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType);
         assertEquals(a.mRequiredAccountType, b.mRequiredAccountType);
         assertEquals(a.mOverlayTarget, b.mOverlayTarget);
+        assertEquals(a.mOverlayCategory, b.mOverlayCategory);
+        assertEquals(a.mOverlayPriority, b.mOverlayPriority);
+        assertEquals(a.mOverlayIsStatic, b.mOverlayIsStatic);
         assertEquals(a.mSigningDetails.publicKeys, b.mSigningDetails.publicKeys);
         assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets);
         assertEquals(a.mKeySetMapping, b.mKeySetMapping);
@@ -524,6 +531,15 @@
         pkg.mCompileSdkVersionCodename = "foo23";
         pkg.mCompileSdkVersion = 100;
         pkg.mVersionCodeMajor = 100;
+
+        pkg.mOverlayCategory = "foo24";
+        pkg.mOverlayIsStatic = true;
+
+        pkg.baseHardwareAccelerated = true;
+        pkg.coreApp = true;
+        pkg.mRequiredForAllUsers = true;
+        pkg.visibleToInstantApps = true;
+        pkg.use32bitAbi = true;
     }
 
     private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception {
@@ -532,6 +548,7 @@
         Set<String> nonSerializedFields = new HashSet<>();
         nonSerializedFields.add("mExtras");
         nonSerializedFields.add("packageUsageTimeMillis");
+        nonSerializedFields.add("isStub");
 
         for (Field f : fields) {
             final Class<?> fieldType = f.getType();
@@ -560,6 +577,10 @@
                 // int fields: Check that they're set to 100.
                 int value = (int) f.get(pkg);
                 assertEquals("Bad value for field: " + f, 100, value);
+            } else if (fieldType == boolean.class) {
+                // boolean fields: Check that they're set to true.
+                boolean value = (boolean) f.get(pkg);
+                assertEquals("Bad value for field: " + f, true, value);
             } else {
                 // All other fields: Check that they're set.
                 Object o = f.get(pkg);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 50be8db..4e1418c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -23,8 +23,9 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.pm.PackageUserState;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
 import org.junit.Test;
@@ -170,4 +171,44 @@
         testUserState03.enabledComponents.add("com.android.unit_test_04");
         assertThat(testUserState03.equals(oldUserState), is(false));
     }
+
+    @Test
+    public void testPackageUserState05() {
+        PersistableBundle appExtras1 = new PersistableBundle();
+        PersistableBundle appExtras2 = new PersistableBundle();
+        appExtras1.putInt("appExtraId", 1);
+        appExtras2.putInt("appExtraId", 2);
+        PersistableBundle launcherExtras1 = new PersistableBundle();
+        PersistableBundle launcherExtras2 = new PersistableBundle();
+        launcherExtras1.putString("name", "launcherExtras1");
+        launcherExtras2.putString("name", "launcherExtras2");
+        final String suspendingPackage1 = "package1";
+        final String suspendingPackage2 = "package2";
+
+        final PackageUserState testUserState1 = new PackageUserState();
+        testUserState1.suspended = true;
+        testUserState1.suspendedAppExtras = appExtras1;
+        testUserState1.suspendedLauncherExtras = launcherExtras1;
+        testUserState1.suspendingPackage = suspendingPackage1;
+
+        final PackageUserState testUserState2 = new PackageUserState(testUserState1);
+        assertThat(testUserState1.equals(testUserState2), is(true));
+        testUserState2.suspendingPackage = suspendingPackage2;
+        assertThat(testUserState1.equals(testUserState2), is(false));
+
+        testUserState2.suspendingPackage = testUserState1.suspendingPackage;
+        testUserState2.suspendedAppExtras = appExtras2;
+        assertThat(testUserState1.equals(testUserState2), is(false));
+
+        testUserState2.suspendedAppExtras = testUserState1.suspendedAppExtras;
+        testUserState2.suspendedLauncherExtras = launcherExtras2;
+        assertThat(testUserState1.equals(testUserState2), is(false));
+
+        // Everything is different but irrelevant if suspended is false
+        testUserState2.suspended = testUserState1.suspended = false;
+        testUserState2.suspendedAppExtras = appExtras2;
+        testUserState2.suspendingPackage = suspendingPackage2;
+        assertThat(testUserState1.equals(testUserState2), is(true));
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
new file mode 100644
index 0000000..d702318
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class SuspendPackagesTest {
+    private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME;
+    private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME};
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private Handler mReceiverHandler;
+    private ComponentName mTestReceiverComponent;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mPackageManager = mContext.getPackageManager();
+        mPackageManager.setPackagesSuspended(PACKAGES_TO_SUSPEND, false, null, null, null);
+        mReceiverHandler = new Handler(Looper.getMainLooper());
+        mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
+                SuspendTestReceiver.class.getCanonicalName());
+    }
+
+    private Bundle requestAppAction(String action) throws InterruptedException {
+        final AtomicReference<Bundle> result = new AtomicReference<>();
+        final CountDownLatch receiverLatch = new CountDownLatch(1);
+
+        final Intent broadcastIntent = new Intent(action)
+                .setComponent(mTestReceiverComponent)
+                .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                result.set(getResultExtras(true));
+                receiverLatch.countDown();
+            }
+        }, mReceiverHandler, 0, null, null);
+
+        assertTrue("Test receiver timed out ", receiverLatch.await(5, TimeUnit.SECONDS));
+        return result.get();
+    }
+
+    private PersistableBundle getExtras(String keyPrefix, long lval, String sval, double dval) {
+        final PersistableBundle extras = new PersistableBundle(3);
+        extras.putLong(keyPrefix + ".LONG_VALUE", lval);
+        extras.putDouble(keyPrefix + ".DOUBLE_VALUE", dval);
+        extras.putString(keyPrefix + ".STRING_VALUE", sval);
+        return extras;
+    }
+
+    private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras) {
+        final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
+                PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, null);
+        assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
+    }
+
+    @Test
+    public void testIsPackageSuspended() {
+        suspendTestPackage(null, null);
+        assertTrue("isPackageSuspended is false",
+                mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
+    }
+
+    @Test
+    public void testSuspendedStateFromApp() throws Exception {
+        Bundle resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
+        assertFalse(resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED, true));
+        assertNull(resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+
+        final PersistableBundle appExtras = getExtras("appExtras", 20, "20", 0.2);
+        suspendTestPackage(appExtras, null);
+
+        resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
+        assertTrue("resultFromApp:suspended is false",
+                resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED));
+        final PersistableBundle receivedAppExtras =
+                resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS);
+        receivedAppExtras.get(""); // hack to unparcel the bundles
+        appExtras.get("");
+        assertTrue("Received app extras " + receivedAppExtras + " different to the ones supplied",
+                BaseBundle.kindofEquals(appExtras, receivedAppExtras));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index f3539fe..56ba047 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -63,7 +63,8 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            @Nullable Rect outsetFrame, WmDisplayCutout displayCutout) {
+            @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
+            boolean parentFrameWasClippedByDisplayCutout) {
         this.parentFrame.set(parentFrame);
         this.displayFrame.set(displayFrame);
         this.overscanFrame.set(overlayFrame);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index edf1f74..552c915 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -17,6 +17,8 @@
 package com.android.server.usage;
 
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
+import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
@@ -406,6 +408,30 @@
     }
 
     @Test
+    public void testSlicePinnedEvent() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        mInjector.mElapsedRealtime = 1;
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+    }
+
+    @Test
+    public void testSlicePinnedPrivEvent() throws Exception {
+        setChargingState(mController, false);
+
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+    }
+
+    @Test
     public void testPredictionTimedout() throws Exception {
         setChargingState(mController, false);
         // Set it to timeout or usage, so that prediction can override it
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
new file mode 100644
index 0000000..8b78f10
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link TaskStack} class.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase {
+
+    @Mock
+    AnimationAdapter mAdapter;
+
+    @Mock
+    Runnable mMockEndDeferFinishCallback1;
+    @Mock
+    Runnable mMockEndDeferFinishCallback2;
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testDeferring() throws Exception {
+        final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
+                "window2").mAppToken;
+        final AnimatingAppWindowTokenRegistry registry =
+                window1.getStack().getAnimatingAppWindowTokenRegistry();
+
+        window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        assertTrue(window1.isSelfAnimating());
+        assertTrue(window2.isSelfAnimating());
+
+        // Make sure that first animation finish is deferred, second one is not deferred, and first
+        // one gets cancelled.
+        assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1));
+        assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2));
+        verify(mMockEndDeferFinishCallback1).run();
+        verifyZeroInteractions(mMockEndDeferFinishCallback2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 4f49a4a..b645700 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,7 +31,10 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -457,6 +460,18 @@
                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
 
+    @Test
+    public void testDisableDisplayInfoOverrideFromWindowManager() {
+        final DisplayContent dc = createNewDisplay();
+
+        assertTrue(dc.mShouldOverrideDisplayConfiguration);
+        sWm.dontOverrideDisplayInfo(dc.getDisplayId());
+
+        assertFalse(dc.mShouldOverrideDisplayConfiguration);
+        verify(sWm.mDisplayManagerInternal, times(1))
+                .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
+    }
+
     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
                              int expectedBaseHeight, int expectedBaseDensity) {
         assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
new file mode 100644
index 0000000..96745fa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -0,0 +1,63 @@
+package com.android.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IPinnedStackListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class PinnedStackControllerTest extends WindowTestsBase {
+
+    @Mock private IPinnedStackListener mIPinnedStackListener;
+    @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub);
+    }
+
+    @Test
+    public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
+        sWm.mSupportsPictureInPicture = true;
+        sWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
+
+        verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
+        verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
+        verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
+                eq(false), anyInt());
+        verify(mIPinnedStackListener).onActionsChanged(any());
+        verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean());
+
+        reset(mIPinnedStackListener);
+
+        final int SHELF_HEIGHT = 300;
+
+        sWm.setShelfHeight(true, SHELF_HEIGHT);
+        verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
+        verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
+                eq(true), anyInt());
+        verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 26a7313..64501e4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -68,6 +68,7 @@
         super.setUp();
         MockitoAnnotations.initMocks(this);
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+        mAdapter.setCallingPid(123);
         sWm.mH.runWithScissors(() -> {
             mHandler = new TestHandler(null, mClock);
         }, 0);
@@ -83,7 +84,7 @@
                     new Point(50, 100), new Rect(50, 100, 150, 150));
             adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
             mController.goodToGo();
-
+            sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -167,4 +168,33 @@
         mController.goodToGo();
         verifyZeroInteractions(mMockRunner);
     }
+
+    @Test
+    public void testNotReallyStarted() throws Exception {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        mController.createAnimationAdapter(win.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        mController.goodToGo();
+        verifyZeroInteractions(mMockRunner);
+    }
+
+    @Test
+    public void testOneNotStarted() throws Exception {
+        final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
+        final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+        mController.createAnimationAdapter(win1.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+        mController.goodToGo();
+        sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+                ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+        verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+        assertEquals(1, appsCaptor.getValue().length);
+        assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index a120eba..16b8458 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl;
@@ -64,6 +65,7 @@
     private SurfaceSession mSession = new SurfaceSession();
     private MyAnimatable mAnimatable;
     private MyAnimatable mAnimatable2;
+    private DeferFinishAnimatable mDeferFinishAnimatable;
 
     @Before
     public void setUp() throws Exception {
@@ -71,6 +73,7 @@
         MockitoAnnotations.initMocks(this);
         mAnimatable = new MyAnimatable();
         mAnimatable2 = new MyAnimatable();
+        mDeferFinishAnimatable = new DeferFinishAnimatable();
     }
 
     @Test
@@ -131,6 +134,7 @@
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verifyZeroInteractions(mSpec);
         assertAnimating(mAnimatable);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
         mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
         verify(mSpec).startAnimation(any(), any(), any());
     }
@@ -166,6 +170,29 @@
         verify(mTransaction).destroy(eq(leash));
     }
 
+    @Test
+    @FlakyTest(detail = "Promote once confirmed non-flaky")
+    public void testDeferFinish() throws Exception {
+
+        // Start animation
+        mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec,
+                true /* hidden */);
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+        assertAnimating(mDeferFinishAnimatable);
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        // Finish the animation but then make sure we are deferring.
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertAnimating(mDeferFinishAnimatable);
+
+        // Now end defer finishing.
+        mDeferFinishAnimatable.endDeferFinishCallback.run();
+        assertNotAnimating(mAnimatable2);
+        assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
+        verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
+    }
+
     private void assertAnimating(MyAnimatable animatable) {
         assertTrue(animatable.mSurfaceAnimator.isAnimating());
         assertNotNull(animatable.mSurfaceAnimator.getAnimation());
@@ -254,4 +281,15 @@
 
         private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true;
     }
+
+    private class DeferFinishAnimatable extends MyAnimatable {
+
+        Runnable endDeferFinishCallback;
+
+        @Override
+        public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+            this.endDeferFinishCallback = endDeferFinishCallback;
+            return true;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index bd212a9..4638635 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -160,7 +160,7 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame,0, 0, 1000, 1000);
         assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
         assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -175,7 +175,7 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 100, 100, 200, 200);
         assertRect(w.mContentInsets, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
@@ -196,7 +196,7 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
@@ -204,14 +204,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertRect(w.mFrame, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertRect(w.mFrame, 0, 0, 300, 300);
@@ -224,14 +224,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -239,7 +239,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -249,16 +249,16 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, 600, 600, 900, 900);
     }
 
@@ -279,7 +279,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT, false);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -291,7 +291,7 @@
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
@@ -308,7 +308,7 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
@@ -340,13 +340,13 @@
 
         final Rect policyCrop = new Rect();
 
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
 
         dcf.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
 
@@ -359,7 +359,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
 
         w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
@@ -396,7 +396,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
-                pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT);
+                pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -415,7 +415,7 @@
 
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
-                cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT);
+                cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
         assertEquals(cf, w.mFrame);
         assertEquals(cf, w.getContentFrameLw());
         assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -433,7 +433,7 @@
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
 
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
 
         assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
         assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
index a3ade1e..5085254 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -36,7 +36,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.proto.WindowManagerTraceProto;
+import com.android.server.wm.WindowManagerTraceProto;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
new file mode 100644
index 0000000..40a34b9
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := SuspendTestApp
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..70a1fd0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.suspendtestapp">
+
+    <application>
+        <activity android:name=".SuspendTestActivity"
+                  android:exported="true" />
+        <receiver android:name=".SuspendTestReceiver"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/ScanSettings.aidl b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
similarity index 60%
copy from wifi/java/android/net/wifi/ScanSettings.aidl
copy to services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
index ebd2a39..fa5fc58 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package com.android.servicestests.apps.suspendtestapp;
 
-parcelable ScanSettings;
+import android.app.Activity;
+
+public class SuspendTestActivity extends Activity {
+    private static final String TAG = SuspendTestActivity.class.getSimpleName();
+
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
new file mode 100644
index 0000000..6f353a0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * 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.servicestests.apps.suspendtestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+public class SuspendTestReceiver extends BroadcastReceiver {
+    private static final String TAG = SuspendTestReceiver.class.getSimpleName();
+
+    public static final String PACKAGE_NAME = "com.android.servicestests.apps.suspendtestapp";
+    public static final String ACTION_GET_SUSPENDED_STATE =
+            PACKAGE_NAME + ".action.GET_SUSPENDED_STATE";
+    public static final String EXTRA_SUSPENDED = PACKAGE_NAME + ".extra.SUSPENDED";
+    public static final String EXTRA_SUSPENDED_APP_EXTRAS =
+            PACKAGE_NAME + ".extra.SUSPENDED_APP_EXTRAS";
+
+    private PackageManager mPm;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mPm = context.getPackageManager();
+        Log.d(TAG, "Received request action " + intent.getAction());
+        switch (intent.getAction()) {
+            case ACTION_GET_SUSPENDED_STATE:
+                final Bundle result = new Bundle();
+                final boolean suspended = mPm.isPackageSuspended();
+                final PersistableBundle appExtras = mPm.getSuspendedPackageAppExtras();
+                result.putBoolean(EXTRA_SUSPENDED, suspended);
+                result.putParcelable(EXTRA_SUSPENDED_APP_EXTRAS, appExtras);
+                setResult(0, null, result);
+                break;
+            default:
+                Log.e(TAG, "Unknown action: " + intent.getAction());
+        }
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index f4313b8..181fceb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -70,6 +70,7 @@
             assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
             assertEquals(getShowBadge(i), ranking.canShowBadge());
             assertEquals(getUserSentiment(i), ranking.getUserSentiment());
+            assertEquals(getHidden(i), ranking.isSuspended());
         }
     }
 
@@ -85,6 +86,7 @@
         Bundle showBadge = new Bundle();
         int[] importance = new int[mKeys.length];
         Bundle userSentiment = new Bundle();
+        Bundle mHidden = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -101,11 +103,12 @@
             snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
             showBadge.putBoolean(key, getShowBadge(i));
             userSentiment.putInt(key, getUserSentiment(i));
+            mHidden.putBoolean(key, getHidden(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden);
         return update;
     }
 
@@ -153,6 +156,10 @@
         return USER_SENTIMENT_NEUTRAL;
     }
 
+    private boolean getHidden(int index) {
+        return index % 2 == 0;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {
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 4fe54b9..b82becd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -68,6 +68,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -264,7 +265,7 @@
                     mPackageManager, mPackageManagerClient, mockLightsManager,
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
-                    mGroupHelper, mAm);
+                    mGroupHelper, mAm, mock(UsageStatsManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -2662,4 +2663,120 @@
 
         assertEquals(expected, actual);
     }
+
+    @Test
+    public void testVisualDifference_diffTitle() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setContentTitle("foo");
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setContentTitle("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffText() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setContentText("foo");
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setContentText("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffProgress() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setProgress(100, 90, false);
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setProgress(100, 100, false);
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertTrue(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testVisualDifference_diffProgressNotDone() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setProgress(100, 90, false);
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setProgress(100, 91, false);
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertFalse(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
+    public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
+        // post 2 notification from this package
+        final NotificationRecord notif1 = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        final NotificationRecord notif2 = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, false);
+        mService.addNotification(notif1);
+        mService.addNotification(notif2);
+
+        // on broadcast, hide the 2 notifications
+        mService.simulatePackageSuspendBroadcast(true, PKG);
+        ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
+        assertEquals(2, captorHide.getValue().size());
+
+        // on broadcast, unhide the 2 notifications
+        mService.simulatePackageSuspendBroadcast(false, PKG);
+        ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
+        assertEquals(2, captorUnhide.getValue().size());
+    }
+
+    @Test
+    public void testNoNotificationsHiddenOnSuspendedPackageBroadcast() {
+        // post 2 notification from this package
+        final NotificationRecord notif1 = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        final NotificationRecord notif2 = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, false);
+        mService.addNotification(notif1);
+        mService.addNotification(notif2);
+
+        // on broadcast, nothing is hidden since no notifications are of package "test_package"
+        mService.simulatePackageSuspendBroadcast(true, "test_package");
+        ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+        verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
+        assertEquals(0, captor.getValue().size());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 4bfb236..0f2e56b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -20,18 +20,27 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
+import android.app.Notification.Person;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
 
 import com.android.server.UiServiceTestCase;
 
@@ -112,5 +121,294 @@
         assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
         assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
     }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_noStyles() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test");
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+
+        assertFalse(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_noStyleToStyle() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test");
+        Notification.Builder n2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_styleToNoStyle() {
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testStyleChangeVisiblyDifferent_changeStyle() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle());
+        Notification.Builder n2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle());
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testInboxTextChange() {
+        Notification.Builder nInbox1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle().addLine("a").addLine("b"));
+        Notification.Builder nInbox2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.InboxStyle().addLine("b").addLine("c"));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nInbox1, nInbox2));
+    }
+
+    @Test
+    public void testBigTextTextChange() {
+        Notification.Builder nBigText1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle().bigText("something"));
+        Notification.Builder nBigText2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle().bigText("else"));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigText1, nBigText2));
+    }
+
+    @Test
+    public void testBigPictureChange() {
+        Notification.Builder nBigPic1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+        Notification.Builder nBigPic2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigPic1, nBigPic2));
+    }
+
+    @Test
+    public void testMessagingChange_text() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class)))
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "b", 100, mock(Notification.Person.class)))
+                );
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_data() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Person.class))
+                                .setData("text", mock(Uri.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Person.class))));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_sender() {
+        Person a = mock(Person.class);
+        when(a.getName()).thenReturn("A");
+        Person b = mock(Person.class);
+        when(b.getName()).thenReturn("b");
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_key() {
+        Person a = mock(Person.class);
+        when(a.getKey()).thenReturn("A");
+        Person b = mock(Person.class);
+        when(b.getKey()).thenReturn("b");
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+
+        assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testMessagingChange_ignoreTimeChange() {
+        Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 100, mock(Notification.Person.class))));
+        Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(new Notification.MessagingStyle.Message(
+                                "a", 1000, mock(Notification.Person.class)))
+                );
+
+        assertFalse(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+    }
+
+    @Test
+    public void testRemoteViews_nullChange() {
+        Notification.Builder n1 = new Notification.Builder(mContext, "test")
+                .setContent(mock(RemoteViews.class));
+        Notification.Builder n2 = new Notification.Builder(mContext, "test");
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test")
+                .setContent(mock(RemoteViews.class));
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test")
+                .setCustomBigContentView(mock(RemoteViews.class));
+        n2 = new Notification.Builder(mContext, "test");
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test")
+                .setCustomBigContentView(mock(RemoteViews.class));
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test");
+        n2 = new Notification.Builder(mContext, "test");
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferent_null() {
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentSame() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentText() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentNumber() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentIntent() {
+        PendingIntent intent1 = mock(PendingIntent.class);
+        PendingIntent intent2 = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent1).build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent2).build())
+                .build();
+
+        assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsMoreOptionsThanChoices() {
+        PendingIntent intent1 = mock(PendingIntent.class);
+        PendingIntent intent2 = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent1).build())
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent1)
+                        .addRemoteInput(new RemoteInput.Builder("a")
+                                .setChoices(new CharSequence[] {"i", "m"})
+                                .build())
+                        .build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent2).build())
+                .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent1).build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
+
+    @Test
+    public void testActionsDifferentRemoteInputs() {
+        PendingIntent intent = mock(PendingIntent.class);
+        Icon icon = mock(Icon.class);
+
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+                        .addRemoteInput(new RemoteInput.Builder("a")
+                                .setChoices(new CharSequence[] {"i", "m"})
+                                .build())
+                        .build())
+                .build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+                        .addRemoteInput(new RemoteInput.Builder("a")
+                                .setChoices(new CharSequence[] {"t", "m"})
+                                .build())
+                        .build())
+                .build();
+
+        assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+    }
 }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 354d2d5..09d88fd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -47,7 +47,9 @@
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.media.AudioAttributes;
@@ -97,6 +99,8 @@
     private static final UserHandle USER = UserHandle.of(0);
     private static final String UPDATED_PKG = "updatedPkg";
     private static final int UID2 = 1111;
+    private static final String SYSTEM_PKG = "android";
+    private static final int SYSTEM_UID= 1000;
     private static final UserHandle USER2 = UserHandle.of(10);
     private static final String TEST_CHANNEL_ID = "test_channel_id";
     private static final String TEST_AUTHORITY = "test";
@@ -136,8 +140,15 @@
         upgrade.targetSdkVersion = Build.VERSION_CODES.O;
         when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
         when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+        when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
         when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
+        when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
+        PackageInfo info = mock(PackageInfo.class);
+        info.signatures = new Signature[] {mock(Signature.class)};
+        when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
+        when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
+                .thenReturn(mock(PackageInfo.class));
         when(mContext.getResources()).thenReturn(
                 InstrumentationRegistry.getContext().getResources());
         when(mContext.getContentResolver()).thenReturn(
@@ -1627,4 +1638,52 @@
         assertEquals(1, retrieved.getChannels().size());
         compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
     }
+
+    @Test
+    public void testAndroidPkgCanBypassDnd_creation() {
+
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+
+        assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+                .canBypassDnd());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(PKG, 1000, test, true);
+
+        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testAndroidPkgCanBypassDnd_update() throws Exception {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);
+
+        assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+                .canBypassDnd());
+
+        // setup + 1st check
+        verify(mPm, times(2)).getPackageInfoAsUser(any(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_update() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG, 1000, test, true);
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG, 1000, update, true);
+        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index be58fd2..381e04c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -49,9 +49,11 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArrayMap;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -102,13 +104,13 @@
                 mConditionProviders));
     }
 
-    private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup)
+    private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup, Integer version)
             throws Exception {
         XmlSerializer serializer = new FastXmlSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
-        mZenModeHelperSpy.writeXml(serializer, forBackup);
+        mZenModeHelperSpy.writeXml(serializer, forBackup, version);
         serializer.endDocument();
         serializer.flush();
         mZenModeHelperSpy.setConfig(new ZenModeConfig(), "writing xml");
@@ -603,7 +605,7 @@
 
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
-        ByteArrayOutputStream baos = writeXmlAndPurge(false);
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
@@ -614,6 +616,96 @@
     }
 
     @Test
+    public void testReadXml() throws Exception {
+        setupZenConfig();
+
+        // automatic zen rule is enabled on upgrade so rules should not be overriden by default
+        ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo weeknights = new ScheduleInfo();
+        customRule.enabled = true;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+        enabledAutoRule.put("customRule", customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule;
+
+        ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+        // set previous version
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, 5);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        assertTrue(mZenModeHelperSpy.mConfig.automaticRules.containsKey("customRule"));
+        setupZenConfigMaintained();
+    }
+
+    @Test
+    public void testReadXmlResetDefaultRules() throws Exception {
+        setupZenConfig();
+
+        // no enabled automatic zen rule, so rules should be overriden by default rules
+        mZenModeHelperSpy.mConfig.automaticRules = new ArrayMap<>();
+
+        // set previous version
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, 5);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        // check default rules
+        ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules;
+        assertTrue(rules.size() != 0);
+        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+            assertTrue(rules.containsKey(defaultId));
+        }
+
+        setupZenConfigMaintained();
+    }
+
+
+    @Test
+    public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
+        setupZenConfig();
+
+        // all automatic zen rules are diabled on upgrade so rules should be overriden by default
+        // rules
+        ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo weeknights = new ScheduleInfo();
+        customRule.enabled = false;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+        enabledAutoRule.put("customRule", customRule);
+        mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule;
+
+        // set previous version
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, 5);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        // check default rules
+        ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules;
+        assertTrue(rules.size() != 0);
+        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+            assertTrue(rules.containsKey(defaultId));
+        }
+        assertFalse(rules.containsKey("customRule"));
+
+        setupZenConfigMaintained();
+    }
+
+    @Test
     public void testPolicyReadsSuppressedEffects() {
         mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
         mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
@@ -622,4 +714,40 @@
         NotificationManager.Policy policy = mZenModeHelperSpy.getNotificationPolicy();
         assertEquals(SUPPRESSED_EFFECT_BADGE, policy.suppressedVisualEffects);
     }
+
+    private void setupZenConfig() {
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mConfig.allowAlarms = false;
+        mZenModeHelperSpy.mConfig.allowMedia = false;
+        mZenModeHelperSpy.mConfig.allowSystem = false;
+        mZenModeHelperSpy.mConfig.allowReminders = true;
+        mZenModeHelperSpy.mConfig.allowCalls = true;
+        mZenModeHelperSpy.mConfig.allowMessages = true;
+        mZenModeHelperSpy.mConfig.allowEvents = true;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
+        mZenModeHelperSpy.mConfig.allowWhenScreenOff = true;
+        mZenModeHelperSpy.mConfig.allowWhenScreenOn = true;
+        mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
+        mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
+        mZenModeHelperSpy.mConfig.manualRule.zenMode =
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
+        mZenModeHelperSpy.mConfig.manualRule.enabled = true;
+        mZenModeHelperSpy.mConfig.manualRule.snoozing = true;
+    }
+
+    private void setupZenConfigMaintained() {
+        // config is still the same as when it was setup (setupZenConfig)
+        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+        assertFalse(mZenModeHelperSpy.mConfig.allowMedia);
+        assertFalse(mZenModeHelperSpy.mConfig.allowSystem);
+        assertTrue(mZenModeHelperSpy.mConfig.allowReminders);
+        assertTrue(mZenModeHelperSpy.mConfig.allowCalls);
+        assertTrue(mZenModeHelperSpy.mConfig.allowMessages);
+        assertTrue(mZenModeHelperSpy.mConfig.allowEvents);
+        assertTrue(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+        assertTrue(mZenModeHelperSpy.mConfig.allowWhenScreenOff);
+        assertTrue(mZenModeHelperSpy.mConfig.allowWhenScreenOn);
+        assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 4f446a9..1073a80 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -30,6 +30,7 @@
 
 import android.app.AppOpsManager;
 import android.app.slice.SliceSpec;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.pm.PackageManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
@@ -66,6 +67,8 @@
     @Before
     public void setup() {
         LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class));
+        LocalServices.addService(UsageStatsManagerInternal.class,
+                mock(UsageStatsManagerInternal.class));
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
 
@@ -77,6 +80,7 @@
     @After
     public void teardown() {
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index e836677..1af5f46 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -36,6 +36,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
@@ -43,8 +44,8 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.usage.AppStandbyInfo;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
@@ -100,7 +101,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
@@ -690,7 +690,9 @@
                     || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
-                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) {
+                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
+                    || event.mEventType == UsageEvents.Event.SLICE_PINNED
+                    || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) {
 
                 final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
                         event.mPackage, userId, elapsedRealtime);
@@ -699,7 +701,8 @@
                 final long nextCheckTime;
                 final int subReason = usageEventToSubReason(event.mEventType);
                 final int reason = REASON_MAIN_USAGE | subReason;
-                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
+                        || event.mEventType == UsageEvents.Event.SLICE_PINNED) {
                     // Mild usage elevates to WORKING_SET but doesn't change usage time.
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
                             STANDBY_BUCKET_WORKING_SET, subReason,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d974282..c2e38f2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -761,6 +761,10 @@
                 return "STANDBY_BUCKET_CHANGED";
             case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
                 return "NOTIFICATION_INTERRUPTION";
+            case UsageEvents.Event.SLICE_PINNED:
+                return "SLICE_PINNED";
+            case UsageEvents.Event.SLICE_PINNED_PRIV:
+                return "SLICE_PINNED_PRIV";
             default:
                 return "UNKNOWN";
         }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index cd3fdee..1160943 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -15,36 +15,68 @@
  */
 
 package com.android.server.soundtrigger;
+
+import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
+import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
+import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.Manifest;
+import android.content.pm.ResolveInfo;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.media.soundtrigger.ISoundTriggerDetectionService;
+import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
+import android.media.soundtrigger.SoundTriggerDetectionService;
 import android.media.soundtrigger.SoundTriggerManager;
+import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.server.SystemService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.TreeMap;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A single SystemService to manage all sound/voice-based sound models on the DSP.
@@ -67,9 +99,13 @@
     private SoundTriggerHelper mSoundTriggerHelper;
     private final TreeMap<UUID, SoundModel> mLoadedModels;
     private Object mCallbacksLock;
-    private final TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback> mIntentCallbacks;
+    private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks;
     private PowerManager.WakeLock mWakelock;
 
+    /** Number of ops run by the {@link RemoteSoundTriggerDetectionService} per package name */
+    @GuardedBy("mLock")
+    private final ArrayMap<String, NumOps> mNumOpsPerPackage = new ArrayMap<>();
+
     public SoundTriggerService(Context context) {
         super(context);
         mContext = context;
@@ -77,7 +113,7 @@
         mLocalSoundTriggerService = new LocalSoundTriggerService(context);
         mLoadedModels = new TreeMap<UUID, SoundModel>();
         mCallbacksLock = new Object();
-        mIntentCallbacks = new TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback>();
+        mCallbacks = new TreeMap<>();
         mLock = new Object();
     }
 
@@ -214,7 +250,7 @@
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid);
                     synchronized (mCallbacksLock) {
-                        mIntentCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.uuid);
                     }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
@@ -245,7 +281,7 @@
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id);
                     synchronized (mCallbacksLock) {
-                        mIntentCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.uuid);
                     }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
@@ -254,8 +290,28 @@
         }
 
         @Override
+        public int startRecognitionForService(ParcelUuid soundModelId, Bundle params,
+            ComponentName detectionService, SoundTrigger.RecognitionConfig config) {
+            Preconditions.checkNotNull(soundModelId);
+            Preconditions.checkNotNull(detectionService);
+            Preconditions.checkNotNull(config);
+
+            return startRecognitionForInt(soundModelId,
+                new RemoteSoundTriggerDetectionService(soundModelId.getUuid(),
+                    params, detectionService, Binder.getCallingUserHandle(), config), config);
+
+        }
+
+        @Override
         public int startRecognitionForIntent(ParcelUuid soundModelId, PendingIntent callbackIntent,
                 SoundTrigger.RecognitionConfig config) {
+            return startRecognitionForInt(soundModelId,
+                new LocalSoundTriggerRecognitionStatusIntentCallback(soundModelId.getUuid(),
+                    callbackIntent, config), config);
+        }
+
+        private int startRecognitionForInt(ParcelUuid soundModelId,
+            IRecognitionStatusCallback callback, SoundTrigger.RecognitionConfig config) {
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return STATUS_ERROR;
             if (DEBUG) {
@@ -268,27 +324,25 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                IRecognitionStatusCallback existingCallback = null;
                 synchronized (mCallbacksLock) {
-                    callback = mIntentCallbacks.get(soundModelId.getUuid());
+                    existingCallback = mCallbacks.get(soundModelId.getUuid());
                 }
-                if (callback != null) {
+                if (existingCallback != null) {
                     Slog.e(TAG, soundModelId + " is already running");
                     return STATUS_ERROR;
                 }
-                callback = new LocalSoundTriggerRecognitionStatusCallback(soundModelId.getUuid(),
-                        callbackIntent, config);
                 int ret;
                 switch (soundModel.type) {
                     case SoundModel.TYPE_KEYPHRASE: {
                         KeyphraseSoundModel keyphraseSoundModel = (KeyphraseSoundModel) soundModel;
                         ret = mSoundTriggerHelper.startKeyphraseRecognition(
-                                keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback,
-                                config);
+                            keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback,
+                            config);
                     } break;
                     case SoundModel.TYPE_GENERIC_SOUND:
                         ret = mSoundTriggerHelper.startGenericRecognition(soundModel.uuid,
-                                (GenericSoundModel) soundModel, callback, config);
+                            (GenericSoundModel) soundModel, callback, config);
                         break;
                     default:
                         Slog.e(TAG, "Unknown model type");
@@ -300,7 +354,7 @@
                     return ret;
                 }
                 synchronized (mCallbacksLock) {
-                    mIntentCallbacks.put(soundModelId.getUuid(), callback);
+                    mCallbacks.put(soundModelId.getUuid(), callback);
                 }
             }
             return STATUS_OK;
@@ -320,9 +374,9 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                IRecognitionStatusCallback callback = null;
                 synchronized (mCallbacksLock) {
-                     callback = mIntentCallbacks.get(soundModelId.getUuid());
+                     callback = mCallbacks.get(soundModelId.getUuid());
                 }
                 if (callback == null) {
                     Slog.e(TAG, soundModelId + " is not running");
@@ -347,7 +401,7 @@
                     return ret;
                 }
                 synchronized (mCallbacksLock) {
-                    mIntentCallbacks.remove(soundModelId.getUuid());
+                    mCallbacks.remove(soundModelId.getUuid());
                 }
             }
             return STATUS_OK;
@@ -394,8 +448,7 @@
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return false;
             synchronized (mCallbacksLock) {
-                LocalSoundTriggerRecognitionStatusCallback callback =
-                        mIntentCallbacks.get(parcelUuid.getUuid());
+                IRecognitionStatusCallback callback = mCallbacks.get(parcelUuid.getUuid());
                 if (callback == null) {
                     return false;
                 }
@@ -404,13 +457,13 @@
         }
     }
 
-    private final class LocalSoundTriggerRecognitionStatusCallback
+    private final class LocalSoundTriggerRecognitionStatusIntentCallback
             extends IRecognitionStatusCallback.Stub {
         private UUID mUuid;
         private PendingIntent mCallbackIntent;
         private RecognitionConfig mRecognitionConfig;
 
-        public LocalSoundTriggerRecognitionStatusCallback(UUID modelUuid,
+        public LocalSoundTriggerRecognitionStatusIntentCallback(UUID modelUuid,
                 PendingIntent callbackIntent,
                 RecognitionConfig config) {
             mUuid = modelUuid;
@@ -528,7 +581,7 @@
         private void removeCallback(boolean releaseWakeLock) {
             mCallbackIntent = null;
             synchronized (mCallbacksLock) {
-                mIntentCallbacks.remove(mUuid);
+                mCallbacks.remove(mUuid);
                 if (releaseWakeLock) {
                     mWakelock.release();
                 }
@@ -536,6 +589,478 @@
         }
     }
 
+    /**
+     * Counts the number of operations added in the last 24 hours.
+     */
+    private static class NumOps {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private int[] mNumOps = new int[24];
+        @GuardedBy("mLock")
+        private long mLastOpsHourSinceBoot;
+
+        /**
+         * Clear buckets of new hours that have elapsed since last operation.
+         *
+         * <p>I.e. when the last operation was triggered at 1:40 and the current operation was
+         * triggered at 4:03, the buckets "2, 3, and 4" are cleared.
+         *
+         * @param currentTime Current elapsed time since boot in ns
+         */
+        void clearOldOps(long currentTime) {
+            synchronized (mLock) {
+                long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
+
+                // Clear buckets of new hours that have elapsed since last operation
+                // I.e. when the last operation was triggered at 1:40 and the current
+                // operation was triggered at 4:03, the bucket "2, 3, and 4" is cleared
+                if (mLastOpsHourSinceBoot != 0) {
+                    for (long hour = mLastOpsHourSinceBoot + 1; hour <= numHoursSinceBoot; hour++) {
+                        mNumOps[(int) (hour % 24)] = 0;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Add a new operation.
+         *
+         * @param currentTime Current elapsed time since boot in ns
+         */
+        void addOp(long currentTime) {
+            synchronized (mLock) {
+                long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
+
+                mNumOps[(int) (numHoursSinceBoot % 24)]++;
+                mLastOpsHourSinceBoot = numHoursSinceBoot;
+            }
+        }
+
+        /**
+         * Get the total operations added in the last 24 hours.
+         *
+         * @return The total number of operations added in the last 24 hours
+         */
+        int getOpsAdded() {
+            synchronized (mLock) {
+                int totalOperationsInLastDay = 0;
+                for (int i = 0; i < 24; i++) {
+                    totalOperationsInLastDay += mNumOps[i];
+                }
+
+                return totalOperationsInLastDay;
+            }
+        }
+    }
+
+    private interface Operation {
+        void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
+    }
+
+    /**
+     * Local end for a {@link SoundTriggerDetectionService}. Operations are queued up and executed
+     * when the service connects.
+     *
+     * <p>If operations take too long they are forcefully aborted.
+     *
+     * <p>This also limits the amount of operations in 24 hours.
+     */
+    private class RemoteSoundTriggerDetectionService
+        extends IRecognitionStatusCallback.Stub implements ServiceConnection {
+        private static final int MSG_STOP_ALL_PENDING_OPERATIONS = 1;
+
+        private final Object mRemoteServiceLock = new Object();
+
+        /** UUID of the model the service is started for */
+        private final @NonNull ParcelUuid mPuuid;
+        /** Params passed into the start method for the service */
+        private final @Nullable Bundle mParams;
+        /** Component name passed when starting the service */
+        private final @NonNull ComponentName mServiceName;
+        /** User that started the service */
+        private final @NonNull UserHandle mUser;
+        /** Configuration of the recognition the service is handling */
+        private final @NonNull RecognitionConfig mRecognitionConfig;
+        /** Wake lock keeping the remote service alive */
+        private final @NonNull PowerManager.WakeLock mRemoteServiceWakeLock;
+
+        private final @NonNull Handler mHandler;
+
+        /** Callbacks that are called by the service */
+        private final @NonNull ISoundTriggerDetectionServiceClient mClient;
+
+        /** Operations that are pending because the service is not yet connected */
+        @GuardedBy("mRemoteServiceLock")
+        private final ArrayList<Operation> mPendingOps = new ArrayList<>();
+        /** Operations that have been send to the service but have no yet finished */
+        @GuardedBy("mRemoteServiceLock")
+        private final ArraySet<Integer> mRunningOpIds = new ArraySet<>();
+        /** The number of operations executed in each of the last 24 hours */
+        private final NumOps mNumOps;
+
+        /** The service binder if connected */
+        @GuardedBy("mRemoteServiceLock")
+        private @Nullable ISoundTriggerDetectionService mService;
+        /** Whether the service has been bound */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mIsBound;
+        /** Whether the service has been destroyed */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mIsDestroyed;
+        /**
+         * Set once a final op is scheduled. No further ops can be added and the service is
+         * destroyed once the op finishes.
+         */
+        @GuardedBy("mRemoteServiceLock")
+        private boolean mDestroyOnceRunningOpsDone;
+
+        /** Total number of operations performed by this service */
+        @GuardedBy("mRemoteServiceLock")
+        private int mNumTotalOpsPerformed;
+
+        /**
+         * Create a new remote sound trigger detection service. This only binds to the service when
+         * operations are in flight. Each operation has a certain time it can run. Once no
+         * operations are allowed to run anymore, {@link #stopAllPendingOperations() all operations
+         * are aborted and stopped} and the service is disconnected.
+         *
+         * @param modelUuid The UUID of the model the recognition is for
+         * @param params The params passed to each method of the service
+         * @param serviceName The component name of the service
+         * @param user The user of the service
+         * @param config The configuration of the recognition
+         */
+        public RemoteSoundTriggerDetectionService(@NonNull UUID modelUuid,
+            @Nullable Bundle params, @NonNull ComponentName serviceName, @NonNull UserHandle user,
+            @NonNull RecognitionConfig config) {
+            mPuuid = new ParcelUuid(modelUuid);
+            mParams = params;
+            mServiceName = serviceName;
+            mUser = user;
+            mRecognitionConfig = config;
+            mHandler = new Handler(Looper.getMainLooper());
+
+            PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE));
+            mRemoteServiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "RemoteSoundTriggerDetectionService " + mServiceName.getPackageName() + ":"
+                            + mServiceName.getClassName());
+
+            synchronized (mLock) {
+                NumOps numOps = mNumOpsPerPackage.get(mServiceName.getPackageName());
+                if (numOps == null) {
+                    numOps = new NumOps();
+                    mNumOpsPerPackage.put(mServiceName.getPackageName(), numOps);
+                }
+                mNumOps = numOps;
+            }
+
+            mClient = new ISoundTriggerDetectionServiceClient.Stub() {
+                @Override
+                public void onOpFinished(int opId) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        synchronized (mRemoteServiceLock) {
+                            mRunningOpIds.remove(opId);
+
+                            if (mRunningOpIds.isEmpty() && mPendingOps.isEmpty()) {
+                                if (mDestroyOnceRunningOpsDone) {
+                                    destroy();
+                                } else {
+                                    disconnectLocked();
+                                }
+                            }
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public boolean pingBinder() {
+            return !(mIsDestroyed || mDestroyOnceRunningOpsDone);
+        }
+
+        /**
+         * Disconnect from the service, but allow to re-connect when new operations are triggered.
+         */
+        private void disconnectLocked() {
+            if (mService != null) {
+                try {
+                    mService.removeClient(mPuuid);
+                } catch (Exception e) {
+                    Slog.e(TAG, mPuuid + ": Cannot remove client", e);
+                }
+
+                mService = null;
+            }
+
+            if (mIsBound) {
+                mContext.unbindService(RemoteSoundTriggerDetectionService.this);
+                mIsBound = false;
+
+                synchronized (mCallbacksLock) {
+                    mRemoteServiceWakeLock.release();
+                }
+            }
+        }
+
+        /**
+         * Disconnect, do not allow to reconnect to the service. All further operations will be
+         * dropped.
+         */
+        private void destroy() {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
+
+            synchronized (mRemoteServiceLock) {
+                disconnectLocked();
+
+                mIsDestroyed = true;
+            }
+
+            // The callback is removed before the flag is set
+            if (!mDestroyOnceRunningOpsDone) {
+                synchronized (mCallbacksLock) {
+                    mCallbacks.remove(mPuuid.getUuid());
+                }
+            }
+        }
+
+        /**
+         * Stop all pending operations and then disconnect for the service.
+         */
+        private void stopAllPendingOperations() {
+            synchronized (mRemoteServiceLock) {
+                if (mIsDestroyed) {
+                    return;
+                }
+
+                if (mService != null) {
+                    int numOps = mRunningOpIds.size();
+                    for (int i = 0; i < numOps; i++) {
+                        try {
+                            mService.onStopOperation(mPuuid, mRunningOpIds.valueAt(i));
+                        } catch (Exception e) {
+                            Slog.e(TAG, mPuuid + ": Could not stop operation "
+                                    + mRunningOpIds.valueAt(i), e);
+                        }
+                    }
+
+                    mRunningOpIds.clear();
+                }
+
+                disconnectLocked();
+            }
+        }
+
+        /**
+         * Verify that the service has the expected properties and then bind to the service
+         */
+        private void bind() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                Intent i = new Intent();
+                i.setComponent(mServiceName);
+
+                ResolveInfo ri = mContext.getPackageManager().resolveServiceAsUser(i,
+                        GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING,
+                        mUser.getIdentifier());
+
+                if (ri == null) {
+                    Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
+                    return;
+                }
+
+                if (!BIND_SOUND_TRIGGER_DETECTION_SERVICE
+                        .equals(ri.serviceInfo.permission)) {
+                    Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
+                            + BIND_SOUND_TRIGGER_DETECTION_SERVICE);
+                    return;
+                }
+
+                mIsBound = mContext.bindServiceAsUser(i, this,
+                        BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser);
+
+                if (mIsBound) {
+                    mRemoteServiceWakeLock.acquire();
+                } else {
+                    Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Run an operation (i.e. send it do the service). If the service is not connected, this
+         * binds the service and then runs the operation once connected.
+         *
+         * @param op The operation to run
+         */
+        private void runOrAddOperation(Operation op) {
+            synchronized (mRemoteServiceLock) {
+                if (mIsDestroyed || mDestroyOnceRunningOpsDone) {
+                    return;
+                }
+
+                if (mService == null) {
+                    mPendingOps.add(op);
+
+                    if (!mIsBound) {
+                        bind();
+                    }
+                } else {
+                    long currentTime = System.nanoTime();
+                    mNumOps.clearOldOps(currentTime);
+
+                    // Drop operation if too many were executed in the last 24 hours.
+                    int opsAllowed = Settings.Global.getInt(mContext.getContentResolver(),
+                            MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                            Integer.MAX_VALUE);
+
+                    int opsAdded = mNumOps.getOpsAdded();
+                    if (mNumOps.getOpsAdded() >= opsAllowed) {
+                        if (DEBUG || opsAllowed + 10 > opsAdded) {
+                            Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were "
+                                    + "run in last 24 hours");
+                        }
+                        return;
+                    }
+
+                    mNumOps.addOp(currentTime);
+
+                    // Find a free opID
+                    int opId = mNumTotalOpsPerformed;
+                    do {
+                        mNumTotalOpsPerformed++;
+                    } while (mRunningOpIds.contains(opId));
+
+                    // Run OP
+                    try {
+                        if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
+
+                        op.run(opId, mService);
+                        mRunningOpIds.add(opId);
+                    } catch (Exception e) {
+                        Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+                    }
+
+                    // Unbind from service if no operations are left (i.e. if the operation failed)
+                    if (mPendingOps.isEmpty() && mRunningOpIds.isEmpty()) {
+                        if (mDestroyOnceRunningOpsDone) {
+                            destroy();
+                        } else {
+                            disconnectLocked();
+                        }
+                    } else {
+                        mHandler.removeMessages(MSG_STOP_ALL_PENDING_OPERATIONS);
+                        mHandler.sendMessageDelayed(obtainMessage(
+                                RemoteSoundTriggerDetectionService::stopAllPendingOperations, this)
+                                        .setWhat(MSG_STOP_ALL_PENDING_OPERATIONS),
+                                Settings.Global.getLong(mContext.getContentResolver(),
+                                        SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+                                        Long.MAX_VALUE));
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) {
+            Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
+                    + ")");
+        }
+
+        @Override
+        public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
+
+            runOrAddOperation((opId, service) -> {
+                if (!mRecognitionConfig.allowMultipleTriggers) {
+                    synchronized (mCallbacksLock) {
+                        mCallbacks.remove(mPuuid.getUuid());
+                    }
+                    mDestroyOnceRunningOpsDone = true;
+                }
+
+                service.onGenericRecognitionEvent(mPuuid, opId, event);
+            });
+        }
+
+        @Override
+        public void onError(int status) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
+
+            runOrAddOperation((opId, service) -> {
+                synchronized (mCallbacksLock) {
+                    mCallbacks.remove(mPuuid.getUuid());
+                }
+                mDestroyOnceRunningOpsDone = true;
+
+                service.onError(mPuuid, opId, status);
+            });
+        }
+
+        @Override
+        public void onRecognitionPaused() {
+            Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
+        }
+
+        @Override
+        public void onRecognitionResumed() {
+            Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
+
+            synchronized (mRemoteServiceLock) {
+                mService = ISoundTriggerDetectionService.Stub.asInterface(service);
+
+                try {
+                    mService.setClient(mPuuid, mParams, mClient);
+                } catch (Exception e) {
+                    Slog.e(TAG, mPuuid + ": Could not init " + mServiceName, e);
+                    return;
+                }
+
+                while (!mPendingOps.isEmpty()) {
+                    runOrAddOperation(mPendingOps.remove(0));
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
+
+            synchronized (mRemoteServiceLock) {
+                mService = null;
+            }
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
+
+            synchronized (mRemoteServiceLock) {
+                destroy();
+            }
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
+
+            synchronized (mRemoteServiceLock) {
+                disconnectLocked();
+            }
+        }
+    }
+
     private void grabWakeLock() {
         synchronized (mCallbacksLock) {
             if (mWakelock == null) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 73243d2..1c0e260 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -881,7 +881,7 @@
          */
         @IntDef(prefix = { "HANDOVER_" },
                 value = {HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_NOT_SUPPORTED,
-                HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERG_CALL,
+                HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL,
                 HANDOVER_FAILURE_UNKNOWN})
         @Retention(RetentionPolicy.SOURCE)
         public @interface HandoverFailureErrors {}
@@ -939,7 +939,7 @@
          * For more information on call handovers, see
          * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
-        public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4;
+        public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4;
 
         /**
          * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when a handover
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index eebe2a1..c79eec0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2227,7 +2227,9 @@
     /**
      * Gets the configuration values for a particular subscription, which is associated with a
      * specific SIM card. If an invalid subId is used, the returned config will contain default
-     * values.
+     * values. After using this method to get the configuration bundle,
+     * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+     * any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2254,7 +2256,9 @@
     }
 
     /**
-     * Gets the configuration values for the default subscription.
+     * Gets the configuration values for the default subscription. After using this method to get
+     * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
+     * called to confirm whether any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2283,6 +2287,9 @@
      * <p>
      * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
      * use this method to confirm whether any carrier specific configuration has been applied.
+     * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
+     * still needs to get the current configuration, it must use this method to verify whether the
+     * configuration is default or carrier overridden.
      * </p>
      *
      * @param bundle the configuration bundle to be checked.
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 105ddb0..713ac00 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -181,6 +182,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -189,6 +191,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 52944a8a..aae7929 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -191,6 +192,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -199,6 +201,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 37fb075..9b3ef56 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -201,6 +202,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -209,6 +211,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 992545d..7475c74 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -34,6 +35,10 @@
     private final int mCid;
     // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
     private final int mCpid;
+    // long alpha Operator Name String or Enhanced Operator Name String
+    private final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    private final String mAlphaShort;
 
     /**
      * @hide
@@ -43,6 +48,8 @@
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
+        mAlphaLong = null;
+        mAlphaShort = null;
     }
 
     /**
@@ -55,7 +62,7 @@
      * @hide
      */
     public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
     }
 
     /**
@@ -65,6 +72,7 @@
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
      *
+     * FIXME: This is a temporary constructor to facilitate migration.
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
@@ -72,10 +80,34 @@
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
+        mAlphaLong = null;
+        mAlphaShort = null;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+            String alphal, String alphas) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
-        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -119,9 +151,31 @@
         return mCpid;
     }
 
+    /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     *
+     * @hide
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     *
+     * @hide
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort);
     }
 
     @Override
@@ -139,7 +193,9 @@
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && mLac == o.mLac
                 && mCid == o.mCid
-                && mCpid == o.mCpid;
+                && mCpid == o.mCpid
+                && mAlphaLong == o.mAlphaLong
+                && mAlphaShort == o.mAlphaShort;
     }
 
     @Override
@@ -150,6 +206,8 @@
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append("}").toString();
     }
 
@@ -161,6 +219,8 @@
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -169,6 +229,8 @@
         mLac = in.readInt();
         mCid = in.readInt();
         mCpid = in.readInt();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index affa0c1..52fa54f 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -182,6 +183,7 @@
      * @return The long alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string). May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaLong() {
         return mAlphaLong;
     }
@@ -190,6 +192,7 @@
      * @return The short alpha tag associated with the current scan result (may be the operator
      * name string or extended operator name string).  May be null if unknown.
      */
+    @Nullable
     public CharSequence getOperatorAlphaShort() {
         return mAlphaShort;
     }
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8a3f138..38bc640 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -343,15 +344,16 @@
      * applications. Intended for internal carrier use only.
      * </p>
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#SEND_SMS} and
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
-     * privileges.
-     * </p>
+     * <p>Requires Permission: Both {@link android.Manifest.permission#SEND_SMS} and
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
+     * the default IMS app (see
+     * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
      */
     @SystemApi
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4a0027b..c5386ef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -111,6 +111,13 @@
             BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
 
     /**
+     * The process name of the Phone app as well as many other apps that use this process name, such
+     * as settings and vendor components.
+     * @hide
+     */
+    public static final String PHONE_PROCESS_NAME = "com.android.phone";
+
+    /**
      * The allowed states of Wi-Fi calling.
      *
      * @hide
@@ -1776,11 +1783,17 @@
      * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain
      * default values.
      *
+     * <p>This method may take several seconds to complete, so it should only be called from a
+     * worker thread.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see CarrierConfigManager#getConfigForSubId(int)
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      */
-    // TODO(b/73136824, b/70041899): Permit carrier-privileged callers as well.
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PersistableBundle getCarrierConfig() {
@@ -1865,47 +1878,52 @@
     /*
      * When adding a network type to the list below, make sure to add the correct icon to
      * MobileSignalController.mapIconSets().
+     * Do not add negative types.
      */
     /** Network type is unknown */
-    public static final int NETWORK_TYPE_UNKNOWN = 0;
+    public static final int NETWORK_TYPE_UNKNOWN = TelephonyProtoEnums.NETWORK_TYPE_UNKNOWN; // = 0.
     /** Current network is GPRS */
-    public static final int NETWORK_TYPE_GPRS = 1;
+    public static final int NETWORK_TYPE_GPRS = TelephonyProtoEnums.NETWORK_TYPE_GPRS; // = 1.
     /** Current network is EDGE */
-    public static final int NETWORK_TYPE_EDGE = 2;
+    public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
     /** Current network is UMTS */
-    public static final int NETWORK_TYPE_UMTS = 3;
+    public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3.
     /** Current network is CDMA: Either IS95A or IS95B*/
-    public static final int NETWORK_TYPE_CDMA = 4;
+    public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4.
     /** Current network is EVDO revision 0*/
-    public static final int NETWORK_TYPE_EVDO_0 = 5;
+    public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5.
     /** Current network is EVDO revision A*/
-    public static final int NETWORK_TYPE_EVDO_A = 6;
+    public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6.
     /** Current network is 1xRTT*/
-    public static final int NETWORK_TYPE_1xRTT = 7;
+    public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
     /** Current network is HSDPA */
-    public static final int NETWORK_TYPE_HSDPA = 8;
+    public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
     /** Current network is HSUPA */
-    public static final int NETWORK_TYPE_HSUPA = 9;
+    public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9.
     /** Current network is HSPA */
-    public static final int NETWORK_TYPE_HSPA = 10;
+    public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10.
     /** Current network is iDen */
-    public static final int NETWORK_TYPE_IDEN = 11;
+    public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
     /** Current network is EVDO revision B*/
-    public static final int NETWORK_TYPE_EVDO_B = 12;
+    public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
     /** Current network is LTE */
-    public static final int NETWORK_TYPE_LTE = 13;
+    public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
     /** Current network is eHRPD */
-    public static final int NETWORK_TYPE_EHRPD = 14;
+    public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
     /** Current network is HSPA+ */
-    public static final int NETWORK_TYPE_HSPAP = 15;
+    public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
     /** Current network is GSM */
-    public static final int NETWORK_TYPE_GSM = 16;
+    public static final int NETWORK_TYPE_GSM = TelephonyProtoEnums.NETWORK_TYPE_GSM; // = 16.
     /** Current network is TD_SCDMA */
-    public static final int NETWORK_TYPE_TD_SCDMA = 17;
+    public static final int NETWORK_TYPE_TD_SCDMA =
+            TelephonyProtoEnums.NETWORK_TYPE_TD_SCDMA; // = 17.
     /** Current network is IWLAN */
-    public static final int NETWORK_TYPE_IWLAN = 18;
+    public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18.
     /** Current network is LTE_CA {@hide} */
-    public static final int NETWORK_TYPE_LTE_CA = 19;
+    public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+
+    /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
+    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
     /**
      * @return the NETWORK_TYPE_xxxx for current data connection.
      */
@@ -5333,24 +5351,6 @@
     }
 
     /**
-     * Determines if emergency calling is allowed for the MMTEL feature on the slot provided.
-     * @param slotIndex The SIM slot of the MMTEL feature
-     * @return true if emergency calling is allowed, false otherwise.
-     * @hide
-     */
-    public boolean isEmergencyMmTelAvailable(int slotIndex) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                return telephony.isEmergencyMmTelAvailable(slotIndex);
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "isEmergencyMmTelAvailable, RemoteException: " + e.getMessage());
-        }
-        return false;
-    }
-
-    /**
      * @return true if the IMS resolver is busy resolving a binding and should not be considered
      * available, false if the IMS resolver is idle.
      * @hide
@@ -5465,7 +5465,10 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
-     * @param executor The executor through which the callback should be invoked.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
@@ -5491,7 +5494,7 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
-        return requestNetworkScan(request, AsyncTask.THREAD_POOL_EXECUTOR, callback);
+        return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 99e2db8..96ff332 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -137,8 +137,10 @@
                             for (int i = 0; i < parcelables.length; i++) {
                                 ci[i] = (CellInfo) parcelables[i];
                             }
-                            executor.execute(() ->
-                                    callback.onResults((List<CellInfo>) Arrays.asList(ci)));
+                            executor.execute(() ->{
+                                Rlog.d(TAG, "onResults: " + ci.toString());
+                                callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onResults", e);
                         }
@@ -146,14 +148,20 @@
                     case CALLBACK_SCAN_ERROR:
                         try {
                             final int errorCode = message.arg1;
-                            executor.execute(() -> callback.onError(errorCode));
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
                         break;
                     case CALLBACK_SCAN_COMPLETE:
                         try {
-                            executor.execute(() -> callback.onComplete());
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
                             mScanInfo.remove(message.arg2);
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.StringDef;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
@@ -28,17 +28,15 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-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.Map;
 import java.util.Objects;
 
 /**
@@ -46,25 +44,184 @@
  */
 public class ApnSetting implements Parcelable {
 
-    static final String LOG_TAG = "ApnSetting";
+    private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+    private static final int NOT_IN_MAP_INT = -1;
+    private static final int NO_PORT_SPECIFIED = -1;
+
+    /** All APN types except IA. */
+    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+    /** APN type for default data traffic and HiPri traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /** APN type for MMS traffic. */
+    public static final int TYPE_MMS = ApnTypes.MMS;
+    /** APN type for SUPL assisted GPS. */
+    public static final int TYPE_SUPL = ApnTypes.SUPL;
+    /** APN type for DUN traffic. */
+    public static final int TYPE_DUN = ApnTypes.DUN;
+    /** APN type for HiPri traffic. */
+    public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+    /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+    public static final int TYPE_FOTA = ApnTypes.FOTA;
+    /** APN type for IMS. */
+    public static final int TYPE_IMS = ApnTypes.IMS;
+    /** APN type for CBS. */
+    public static final int TYPE_CBS = ApnTypes.CBS;
+    /** APN type for IA Initial Attach APN. */
+    public static final int TYPE_IA = ApnTypes.IA;
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     */
+    public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+        TYPE_DEFAULT,
+        TYPE_MMS,
+        TYPE_SUPL,
+        TYPE_DUN,
+        TYPE_HIPRI,
+        TYPE_FOTA,
+        TYPE_IMS,
+        TYPE_CBS,
+        TYPE_IA,
+        TYPE_EMERGENCY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    // Possible values for authentication types.
+    /** No authentication type. */
+    public static final int AUTH_TYPE_NONE = 0;
+    /** Authentication type for PAP. */
+    public static final int AUTH_TYPE_PAP = 1;
+    /** Authentication type for CHAP. */
+    public static final int AUTH_TYPE_CHAP = 2;
+    /** Authentication type for PAP or CHAP. */
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol.
+    /** Protocol type for IP. */
+    public static final int PROTOCOL_IP = 0;
+    /** Protocol type for IPV6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Protocol type for IPV4V6. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Protocol type for PPP. */
+    public static final int PROTOCOL_PPP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /** MVNO type for service provider name. */
+    public static final int MVNO_TYPE_SPN = 0;
+    /** MVNO type for IMSI. */
+    public static final int MVNO_TYPE_IMSI = 1;
+    /** MVNO type for group identifier level 1. */
+    public static final int MVNO_TYPE_GID = 2;
+    /** MVNO type for ICCID. */
+    public static final int MVNO_TYPE_ICCID = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+        MVNO_TYPE_SPN,
+        MVNO_TYPE_IMSI,
+        MVNO_TYPE_GID,
+        MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+        PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+        PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+        PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+        PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+        PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+        MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+        MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+        MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+        MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+    }
+
     private final String mEntryName;
     private final String mApnName;
-    private final InetAddress mProxy;
-    private final int mPort;
-    private final URL mMmsc;
-    private final InetAddress mMmsProxy;
-    private final int mMmsPort;
+    private final InetAddress mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final InetAddress mMmsProxyAddress;
+    private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
     private final int mAuthType;
-    private final List<String> mTypes;
-    private final int mTypesBitmap;
+    private final int mApnTypeBitmask;
     private final int mId;
     private final String mOperatorNumeric;
-    private final String mProtocol;
-    private final String mRoamingProtocol;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
     private final int mWaitTime;
     private final int mMaxConnsTime;
 
-    private final String mMvnoType;
+    private final int mMvnoType;
     private final String mMvnoMatchData;
 
     private boolean mPermanentFailed = false;
 
     /**
-     * Returns the types bitmap of the APN.
-     *
-     * @return types bitmap of the APN
-     * @hide
-     */
-    public int getTypesBitmap() {
-        return mTypesBitmap;
-    }
-
-    /**
      * Returns the MTU size of the mobile interface to which the APN connected.
      *
      * @return the MTU size of the APN
@@ -211,8 +358,8 @@
      *
      * @return proxy address.
      */
-    public InetAddress getProxy() {
-        return mProxy;
+    public InetAddress getProxyAddress() {
+        return mProxyAddress;
     }
 
     /**
@@ -220,15 +367,15 @@
      *
      * @return proxy port
      */
-    public int getPort() {
-        return mPort;
+    public int getProxyPort() {
+        return mProxyPort;
     }
     /**
-     * Returns the MMSC URL of the APN.
+     * Returns the MMSC Uri of the APN.
      *
-     * @return MMSC URL.
+     * @return MMSC Uri.
      */
-    public URL getMmsc() {
+    public Uri getMmsc() {
         return mMmsc;
     }
 
@@ -237,8 +384,8 @@
      *
      * @return MMS proxy address.
      */
-    public InetAddress getMmsProxy() {
-        return mMmsProxy;
+    public InetAddress getMmsProxyAddress() {
+        return mMmsProxyAddress;
     }
 
     /**
@@ -246,8 +393,8 @@
      *
      * @return MMS proxy port
      */
-    public int getMmsPort() {
-        return mMmsPort;
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
     }
 
     /**
@@ -268,21 +415,9 @@
         return mPassword;
     }
 
-    /** @hide */
-    @IntDef({
-            AUTH_TYPE_NONE,
-            AUTH_TYPE_PAP,
-            AUTH_TYPE_CHAP,
-            AUTH_TYPE_PAP_OR_CHAP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AuthType {}
-
     /**
      * Returns the authentication type of the APN.
      *
-     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-     *
      * @return authentication type
      */
     @AuthType
@@ -290,32 +425,20 @@
         return mAuthType;
     }
 
-    /** @hide */
-    @StringDef({
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ApnType {}
-
     /**
-     * Returns the list of APN types of the APN.
+     * Returns the bitmask of APN types.
      *
-     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+     * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+     * MMS-specific connections.
      *
-     * @return the list of APN types
+     * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+     *
+     * @see Builder#setApnTypeBitmask(int)
+     * @return a bitmask describing the types of the APN
      */
-    @ApnType
-    public List<String> getTypes() {
-        return mTypes;
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
     }
 
     /**
@@ -328,7 +451,7 @@
     }
 
     /**
-     * Returns the numeric operator ID for the APN. Usually
+     * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
      * {@link android.provider.Telephony.Carriers#MCC} +
      * {@link android.provider.Telephony.Carriers#MNC}.
      *
@@ -338,37 +461,29 @@
         return mOperatorNumeric;
     }
 
-    /** @hide */
-    @StringDef({
-            PROTOCOL_IP,
-            PROTOCOL_IPV6,
-            PROTOCOL_IPV4V6,
-            PROTOCOL_PPP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProtocolType {}
-
     /**
      * Returns the protocol to use to connect to this APN.
      *
-     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setProtocol(int)
      * @return the protocol
      */
     @ProtocolType
-    public String getProtocol() {
+    public int getProtocol() {
         return mProtocol;
     }
 
     /**
-     * Returns the protocol to use to connect to this APN when roaming.
+     * Returns the protocol to use to connect to this APN while the device is roaming.
      *
-     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setRoamingProtocol(int)
      * @return the roaming protocol
      */
-    public String getRoamingProtocol() {
+    @ProtocolType
+    public int getRoamingProtocol() {
         return mRoamingProtocol;
     }
 
@@ -398,41 +513,29 @@
         return mNetworkTypeBitmask;
     }
 
-    /** @hide */
-    @StringDef({
-            MVNO_TYPE_SPN,
-            MVNO_TYPE_IMSI,
-            MVNO_TYPE_GID,
-            MVNO_TYPE_ICCID,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MvnoType {}
-
     /**
      * Returns the MVNO match type for this APN.
      *
-     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-     *
+     * @see Builder#setMvnoType(int)
      * @return the MVNO match type
      */
     @MvnoType
-    public String getMvnoType() {
+    public int getMvnoType() {
         return mMvnoType;
     }
 
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
-        this.mProxy = builder.mProxy;
-        this.mPort = builder.mPort;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
         this.mMmsc = builder.mMmsc;
-        this.mMmsProxy = builder.mMmsProxy;
-        this.mMmsPort = builder.mMmsPort;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
         this.mUser = builder.mUser;
         this.mPassword = builder.mPassword;
         this.mAuthType = builder.mAuthType;
-        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
-        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
         this.mId = builder.mId;
         this.mOperatorNumeric = builder.mOperatorNumeric;
         this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
 
     /** @hide */
     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,
+            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+            int protocol, int roamingProtocol, boolean carrierEnabled,
             int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxy(proxy)
-                .setPort(port)
+                .setProxyAddress(proxy)
+                .setProxyPort(port)
                 .setMmsc(mmsc)
-                .setMmsProxy(mmsProxy)
-                .setMmsPort(mmsPort)
+                .setMmsProxyAddress(mmsProxy)
+                .setMmsProxyPort(mmsPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
-                .setTypes(types)
+                .setApnTypeBitmask(mApnTypeBitmask)
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
+        final int apnTypesBitmask = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
-                URLFromString(cursor.getString(
+                UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
                 inetAddressFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                Arrays.asList(types),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
+                apnTypesBitmask,
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.ROAMING_PROTOCOL)))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -531,8 +636,9 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)),
+                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MVNO_MATCH_DATA)));
     }
@@ -540,8 +646,8 @@
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         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.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
                 apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxy))
-                .append(", ").append(URLToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxy))
-                .append(", ").append(portToString(mMmsPort))
-                .append(", ").append(portToString(mPort))
+                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        for (int i = 0; i < mTypes.size(); i++) {
-            sb.append(mTypes.get(i));
-            if (i < mTypes.size() - 1) {
-                sb.append(" | ");
-            }
-        }
+        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types)).append(", ");
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
     }
 
     /** @hide */
-    public boolean canHandleType(String type) {
-        if (!mCarrierEnabled) return false;
-        boolean wildcardable = true;
-        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : mTypes) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type)
-                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
-                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
-                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean canHandleType(@ApnType int type) {
+        return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
     }
 
     // check whether the types of two APN same (even only one type of each APN is same)
     private boolean typeSameAny(ApnSetting first, ApnSetting second) {
         if (VDBG) {
             StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
-            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-                apnType1.append(first.mTypes.get(index1));
-                apnType1.append(",");
-            }
+            apnType1.append(deParseTypes(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
-                apnType2.append(second.mTypes.get(index1));
-                apnType2.append(",");
-            }
+            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
         }
 
-        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
-                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
-                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
-                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
+        if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+            if (VDBG) {
+                Rlog.d(LOG_TAG, "typeSameAny: return true");
             }
+            return true;
         }
 
-        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
         return false;
     }
 
@@ -655,16 +738,15 @@
                 && Objects.equals(mId, other.mId)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort,other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort,other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
         return mEntryName.equals(other.mEntryName)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort, other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
                 && !other.canHandleType(TYPE_DUN)
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
-                && xorEqualsInetAddress(this.mProxy, other.mProxy)
-                && xorEqualsPort(this.mPort, other.mPort)
+                && xorEquals(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
                 && 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))
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
                 && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
@@ -757,42 +838,23 @@
                 || TextUtils.isEmpty(second));
     }
 
-    // Equal or one is not specified.
-    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
-        return first == null || second == null || first.equals(second);
-    }
-
-    // Equal or one is not specified.
-    private boolean xorEqualsURL(URL first, URL second) {
+    // Equal or one is not null.
+    private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
     private boolean xorEqualsPort(int first, int second) {
-        return first == -1 || second == -1 || Objects.equals(first, second);
+        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+            || Objects.equals(first, second);
     }
 
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case TYPE_MMS: return ApnTypes.MMS;
-            case TYPE_SUPL: return ApnTypes.SUPL;
-            case TYPE_DUN: return ApnTypes.DUN;
-            case TYPE_HIPRI: return ApnTypes.HIPRI;
-            case TYPE_FOTA: return ApnTypes.FOTA;
-            case TYPE_IMS: return ApnTypes.IMS;
-            case TYPE_CBS: return ApnTypes.CBS;
-            case TYPE_IA: return ApnTypes.IA;
-            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-
-    private String deParseTypes(List<String> types) {
-        if (types == null) {
-            return null;
+    private String deParseTypes(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
         }
         return TextUtils.join(",", types);
     }
@@ -808,21 +870,25 @@
         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));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
-                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+            : inetAddressToString(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+                ? "" : inetAddressToString(mMmsProxyAddress));
         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);
+        String apnType = deParseTypes(mApnTypeBitmask);
         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.PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_TYPE,
+            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
         apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
@@ -830,32 +896,31 @@
 
     /**
      * @param types comma delimited list of APN types
-     * @return array of APN types
+     * @return bitmask of APN types
      * @hide
      */
-    public static String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
+    public static int parseTypes(String types) {
+        // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            result = new String[1];
-            result[0] = TYPE_ALL;
+            return TYPE_ALL_BUT_IA;
         } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
-    private static URL URLFromString(String url) {
-        try {
-            return TextUtils.isEmpty(url) ? null : new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOG_TAG, "Can't parse URL from string.");
-            return null;
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str);
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
         }
     }
 
-    private static String URLToString(URL url) {
-        return url == null ? "" : url.toString();
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? "" : uri.toString();
     }
 
     private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = -1;
+        int port = NO_PORT_SPECIFIED;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
     }
 
     private static String portToString(int port) {
-        return port == -1 ? "" : Integer.toString(port);
+        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -916,19 +981,19 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxy);
-        dest.writeInt(mPort);
+        dest.writeValue(mProxyAddress);
+        dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxy);
-        dest.writeInt(mMmsPort);
+        dest.writeValue(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
         dest.writeInt(mAuthType);
-        dest.writeStringArray(mTypes.toArray(new String[0]));
-        dest.writeString(mProtocol);
-        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
-        dest.writeString(mMvnoType);
+        dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
 
@@ -939,23 +1004,23 @@
         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 Uri mmsc = (Uri)in.readValue(Uri.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 int apnTypesBitmask = in.readInt();
+        final int protocol = in.readInt();
+        final int roamingProtocol = in.readInt();
         final boolean carrierEnabled = in.readInt() > 0;
-        final String mvnoType = in.readString();
+        final int mvnoType = in.readInt();
         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);
+            proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+            0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
                 }
             };
 
-    /**
-     * APN types for data connections.  These are usage categories for an APN
-     * entry.  One APN entry may support multiple APN types, eg, a single APN
-     * may service regular internet traffic ("default") as well as MMS-specific
-     * connections.<br/>
-     * ALL is a special type to indicate that this APN entry can
-     * service all data connections.
-     */
-    public static final String TYPE_ALL = "*";
-    /** APN type for default data traffic */
-    public static final String TYPE_DEFAULT = "default";
-    /** APN type for MMS traffic */
-    public static final String TYPE_MMS = "mms";
-    /** APN type for SUPL assisted GPS */
-    public static final String TYPE_SUPL = "supl";
-    /** APN type for DUN traffic */
-    public static final String TYPE_DUN = "dun";
-    /** APN type for HiPri traffic */
-    public static final String TYPE_HIPRI = "hipri";
-    /** APN type for FOTA */
-    public static final String TYPE_FOTA = "fota";
-    /** APN type for IMS */
-    public static final String TYPE_IMS = "ims";
-    /** APN type for CBS */
-    public static final String TYPE_CBS = "cbs";
-    /** APN type for IA Initial Attach APN */
-    public static final String TYPE_IA = "ia";
-    /** APN type for Emergency PDN. This is not an IA apn, but is used
-     * for access to carrier services in an emergency call situation. */
-    public static final String TYPE_EMERGENCY = "emergency";
-    /**
-     * Array of all APN types
-     *
-     * @hide
-     */
-    public static final String[] ALL_TYPES = {
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    };
-
-    // Possible values for authentication types.
-    public static final int AUTH_TYPE_NONE = 0;
-    public static final int AUTH_TYPE_PAP = 1;
-    public static final int AUTH_TYPE_CHAP = 2;
-    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
-    // Possible values for protocol.
-    public static final String PROTOCOL_IP = "IP";
-    public static final String PROTOCOL_IPV6 = "IPV6";
-    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
-    public static final String PROTOCOL_PPP = "PPP";
-
-    // Possible values for MVNO type.
-    public static final String MVNO_TYPE_SPN = "spn";
-    public static final String MVNO_TYPE_IMSI = "imsi";
-    public static final String MVNO_TYPE_GID = "gid";
-    public static final String MVNO_TYPE_ICCID = "iccid";
+    private static int nullToNotInMapInt(Integer value) {
+        return value == null ? NOT_IN_MAP_INT : value;
+    }
 
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxy;
-        private int mPort = -1;
-        private URL mMmsc;
-        private InetAddress mMmsProxy;
-        private int mMmsPort = -1;
+        private InetAddress mProxyAddress;
+        private int mProxyPort = NO_PORT_SPECIFIED;
+        private Uri mMmsc;
+        private InetAddress mMmsProxyAddress;
+        private int mMmsProxyPort = NO_PORT_SPECIFIED;
         private String mUser;
         private String mPassword;
         private int mAuthType;
-        private List<String> mTypes;
-        private int mTypesBitmap;
+        private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private String mProtocol;
-        private String mRoamingProtocol;
+        private int mProtocol = NOT_IN_MAP_INT;
+        private int mRoamingProtocol = NOT_IN_MAP_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private String mMvnoType;
+        private int mMvnoType = NOT_IN_MAP_INT;
         private String mMvnoMatchData;
 
         /**
@@ -1182,8 +1184,8 @@
          *
          * @param proxy the proxy address to set for the APN
          */
-        public Builder setProxy(InetAddress proxy) {
-            this.mProxy = proxy;
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = proxy;
             return this;
         }
 
@@ -1192,17 +1194,17 @@
          *
          * @param port the proxy port to set for the APN
          */
-        public Builder setPort(int port) {
-            this.mPort = port;
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
             return this;
         }
 
         /**
-         * Sets the MMSC URL of the APN.
+         * Sets the MMSC Uri of the APN.
          *
-         * @param mmsc the MMSC URL to set for the APN
+         * @param mmsc the MMSC Uri to set for the APN
          */
-        public Builder setMmsc(URL mmsc) {
+        public Builder setMmsc(Uri mmsc) {
             this.mMmsc = mmsc;
             return this;
         }
@@ -1212,8 +1214,8 @@
          *
          * @param mmsProxy the MMS proxy address to set for the APN
          */
-        public Builder setMmsProxy(InetAddress mmsProxy) {
-            this.mMmsProxy = mmsProxy;
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
             return this;
         }
 
@@ -1222,8 +1224,8 @@
          *
          * @param mmsPort the MMS proxy port to set for the APN
          */
-        public Builder setMmsPort(int mmsPort) {
-            this.mMmsPort = mmsPort;
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
             return this;
         }
 
@@ -1251,8 +1253,6 @@
         /**
          * Sets the authentication type of the APN.
          *
-         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-         *
          * @param authType the authentication type to set for the APN
          */
         public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
         }
 
         /**
-         * Sets the list of APN types of the APN.
+         * Sets the bitmask of APN types.
          *
-         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+         * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+         * MMS-specific connections.
          *
-         * @param types the list of APN types to set for the APN
+         * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+         *
+         * @param apnTypeBitmask a bitmask describing the types of the APN
          */
-        public Builder setTypes(@ApnType List<String> types) {
-            this.mTypes = types;
-            int apnBitmap = 0;
-            for (int i = 0; i < mTypes.size(); i++) {
-                mTypes.set(i, mTypes.get(i).toLowerCase());
-                apnBitmap |= getApnBitmask(mTypes.get(i));
-            }
-            this.mTypesBitmap = apnBitmap;
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
             return this;
         }
 
         /**
-         * Set the numeric operator ID for the APN.
+         * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+         * {@link android.provider.Telephony.Carriers#MCC} +
+         * {@link android.provider.Telephony.Carriers#MNC}.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
          */
@@ -1291,22 +1291,23 @@
         /**
          * Sets the protocol to use to connect to this APN.
          *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param protocol the protocol to set to use to connect to this APN
          */
-        public Builder setProtocol(@ProtocolType String protocol) {
+        public Builder setProtocol(@ProtocolType int protocol) {
             this.mProtocol = protocol;
             return this;
         }
 
         /**
-         * Sets the protocol to use to connect to this APN when roaming.
+         * Sets the protocol to use to connect to this APN when the device is roaming.
+         *
+         * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
          */
-        public Builder setRoamingProtocol(String roamingProtocol) {
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
             this.mRoamingProtocol = roamingProtocol;
             return this;
         }
@@ -1334,16 +1335,25 @@
         /**
          * Sets the MVNO match type for this APN.
          *
-         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-         *
          * @param mvnoType the MVNO match type to set for this APN
          */
-        public Builder setMvnoType(@MvnoType String mvnoType) {
+        public Builder setMvnoType(@MvnoType int mvnoType) {
             this.mMvnoType = mvnoType;
             return this;
         }
 
+        /**
+         * Builds {@link ApnSetting} from this builder.
+         *
+         * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+         * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+         * {@link ApnSetting} built from this builder otherwise.
+         */
         public ApnSetting build() {
+            if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+                || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
             return new ApnSetting(this);
         }
     }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 1637c55..dff1c6f 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -15,11 +15,12 @@
  */
 package android.telephony.euicc;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -73,6 +74,7 @@
      */
     @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public static final String ACTION_OTA_STATUS_CHANGED =
             "android.telephony.euicc.action.OTA_STATUS_CHANGED";
 
@@ -301,6 +303,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -325,6 +328,7 @@
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -387,6 +391,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
         if (!isEnabled()) {
             PendingIntent callbackIntent =
@@ -422,6 +427,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -452,6 +458,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -496,6 +503,7 @@
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -523,6 +531,7 @@
      *     current profile without activating another profile to replace it.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -548,6 +557,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -566,12 +576,13 @@
      * Erase all subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index c073d1a..aaf1a1cf8 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -139,34 +139,44 @@
 
         @Override
         public int queryCapabilityStatus() throws RemoteException {
-            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            synchronized (mLock) {
+                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            }
         }
 
         @Override
         public void addCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
             MmTelFeature.this.addCapabilityCallback(c);
         }
 
         @Override
         public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
             MmTelFeature.this.removeCapabilityCallback(c);
         }
 
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
                 IImsCapabilityCallback c) throws RemoteException {
-            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            synchronized (mLock) {
+                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            }
         }
 
         @Override
         public void queryCapabilityConfiguration(int capability, int radioTech,
                 IImsCapabilityCallback c) {
-            queryCapabilityConfigurationInternal(capability, radioTech, c);
+            synchronized (mLock) {
+                queryCapabilityConfigurationInternal(capability, radioTech, c);
+            }
         }
 
         @Override
         public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            MmTelFeature.this.setSmsListener(l);
+            synchronized (mLock) {
+                MmTelFeature.this.setSmsListener(l);
+            }
         }
 
         @Override
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 7c7700b..fbb69ad 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -823,12 +823,6 @@
     IImsConfig getImsConfig(int slotId, int feature);
 
     /**
-    * Returns true if emergency calling is available for the MMTEL feature associated with the
-    * slot specified.
-    */
-    boolean isEmergencyMmTelAvailable(int slotId);
-
-    /**
      * @return true if the IMS resolver is busy resolving a binding and should not be considered
      * available, false if the IMS resolver is idle.
      */
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 14c5f4b..964a313 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -470,6 +470,9 @@
         int bearerDataLength;
         SmsEnvelope env = new SmsEnvelope();
         CdmaSmsAddress addr = new CdmaSmsAddress();
+        // We currently do not parse subaddress in PDU, but it is required when determining
+        // fingerprint (see getIncomingSmsFingerprint()).
+        CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
 
         try {
             env.messageType = dis.readInt();
@@ -520,6 +523,7 @@
         // link the filled objects to this SMS
         mOriginatingAddress = addr;
         env.origAddress = addr;
+        env.origSubaddress = subaddr;
         mEnvelope = env;
         mPdu = pdu;
 
@@ -1009,8 +1013,11 @@
         output.write(mEnvelope.teleService);
         output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
         output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
-        output.write(mEnvelope.origSubaddress.origBytes, 0,
-                mEnvelope.origSubaddress.origBytes.length);
+        // subaddress is not set when parsing some MT SMS.
+        if (mEnvelope.origSubaddress != null && mEnvelope.origSubaddress.origBytes != null) {
+            output.write(mEnvelope.origSubaddress.origBytes, 0,
+                    mEnvelope.origSubaddress.origBytes.length);
+        }
 
         return output.toByteArray();
     }
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 07acfef..f3b253c 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -278,6 +278,7 @@
     method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+    method public android.content.pm.ResolveInfo resolveServiceAsUser(android.content.Intent, int, int);
     method public void setApplicationCategoryHint(java.lang.String, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 1af7c3a..c2aca6b 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -53,6 +54,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
@@ -464,6 +466,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         throw new UnsupportedOperationException();
     }
@@ -945,7 +952,8 @@
 
     /** @hide */
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean hidden, int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean hidden,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
index a2427f5..35f1e58 100644
--- a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
+++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
@@ -16,7 +16,10 @@
 package com.android.tests.bandwidthenforcement;
 
 import android.app.IntentService;
+import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
 import android.net.SntpClient;
 import android.os.Environment;
 import android.util.Log;
@@ -55,7 +58,7 @@
         String outputFile = intent.getStringExtra(OUTPUT_FILE);
         dumpResult("testUrlConnection", testUrlConnection(), outputFile);
         dumpResult("testUrlConnectionv6", testUrlConnectionv6(), outputFile);
-        dumpResult("testSntp", testSntp(), outputFile);
+        dumpResult("testSntp", testSntp(getApplicationContext()), outputFile);
         dumpResult("testDns", testDns(), outputFile);
     }
 
@@ -138,9 +141,12 @@
      * Tests to connect via sntp.
      * @return true if it was able to connect, false otherwise.
      */
-    public static boolean testSntp() {
+    public static boolean testSntp(Context context) {
         final SntpClient client = new SntpClient();
-        if (client.requestTime("0.pool.ntp.org", 10000)) {
+        final ConnectivityManager mCM = context.getSystemService(ConnectivityManager.class);
+        final Network network = mCM.getActiveNetwork();
+
+        if (client.requestTime("0.pool.ntp.org", 10000, network)) {
             return true;
         }
         return false;
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index cc3366f..0ca20de 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -50,13 +50,18 @@
 
     private static final int TEST_UDP_ENCAP_PORT = 34567;
     private static final int DROID_SPI = 0xD1201D;
+    private static final int DUMMY_RESOURCE_ID = 0x1234;
 
     private static final InetAddress GOOGLE_DNS_4;
+    private static final String VTI_INTF_NAME = "ipsec_test";
+    private static final InetAddress VTI_LOCAL_ADDRESS;
+    private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24");
 
     static {
         try {
             // Google Public DNS Addresses;
             GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8");
+            VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4");
         } catch (UnknownHostException e) {
             throw new RuntimeException("Could not resolve DNS Addresses", e);
         }
@@ -77,9 +82,8 @@
      */
     @Test
     public void testAllocSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(DROID_SPI),
@@ -92,14 +96,13 @@
 
         droidSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     @Test
     public void testAllocRandomSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
@@ -113,7 +116,7 @@
 
         randomSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     /*
@@ -165,11 +168,10 @@
 
     @Test
     public void testOpenEncapsulationSocket() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
         when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject()))
@@ -182,16 +184,15 @@
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
     }
 
     @Test
     public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
 
@@ -206,7 +207,7 @@
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
     }
 
     @Test
@@ -219,4 +220,45 @@
     }
 
     // TODO: add test when applicable transform builder interface is available
-}
+
+    private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName)
+            throws Exception {
+        IpSecTunnelInterfaceResponse dummyResponse =
+                new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+        when(mMockIpSecService.createTunnelInterface(
+                eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()),
+                anyObject(), anyObject()))
+                        .thenReturn(dummyResponse);
+
+        IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface(
+                VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class));
+
+        assertNotNull(tunnelIntf);
+        return tunnelIntf;
+    }
+
+    @Test
+    public void testCreateVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName());
+
+        tunnelIntf.close();
+        verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID));
+    }
+
+    @Test
+    public void testAddRemoveAddressesFromVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        tunnelIntf.addAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+
+        tunnelIntf.removeAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 3e1ff6d..410f754 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -17,11 +17,13 @@
 package com.android.server;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +34,9 @@
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
+import android.net.Network;
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
@@ -56,10 +61,15 @@
 
     private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final LinkAddress mLocalInnerAddress;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
-        return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
+        return Arrays.asList(
+                new Object[][] {
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
+                {"2601::2", "2601::10", "2001:db8::1/64"}
+        });
     }
 
     private static final byte[] AEAD_KEY = {
@@ -86,6 +96,7 @@
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
+    Network fakeNetwork = new Network(0xAB);
 
     private static final IpSecAlgorithm AUTH_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
@@ -94,9 +105,11 @@
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
-    public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+    public IpSecServiceParameterizedTest(
+            String sourceAddr, String destAddr, String localInnerAddr) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
+        mLocalInnerAddress = new LinkAddress(localInnerAddr);
     }
 
     @Before
@@ -308,6 +321,30 @@
     }
 
     @Test
+    public void testReleaseOwnedSpi() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        verify(mMockNetd, times(0))
+                .ipSecDeleteSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt());
+        // quota is not released until the SPI is released by the Transform
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+    }
+
+    @Test
     public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
@@ -317,7 +354,7 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder());
         mIpSecService.deleteTransform(createTransformResp.resourceId);
 
-        verify(mMockNetd)
+        verify(mMockNetd, times(1))
                 .ipSecDeleteSecurityAssociation(
                         eq(createTransformResp.resourceId),
                         anyString(),
@@ -330,6 +367,21 @@
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        // Verify that ipSecDeleteSa was not called when the SPI was released because the
+        // ownedByTransform property should prevent it; (note, the called count is cumulative).
+        verify(mMockNetd, times(1))
+                .ipSecDeleteSecurityAssociation(
+                        anyInt(),
+                        anyString(),
+                        anyString(),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+        assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
+
         try {
             userRecord.mTransformRecords.getRefcountedResourceOrThrow(
                     createTransformResp.resourceId);
@@ -406,4 +458,103 @@
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
+
+    private IpSecTunnelInterfaceResponse createAndValidateTunnel(
+            String localAddr, String remoteAddr) {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                mIpSecService.createTunnelInterface(
+                        mSourceAddr, mDestinationAddr, fakeNetwork, new Binder());
+
+        assertNotNull(createTunnelResp);
+        assertEquals(IpSecManager.Status.OK, createTunnelResp.status);
+        return createTunnelResp;
+    }
+
+    @Test
+    public void testCreateTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        // Check that we have stored the tracking object, and retrieve it
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd)
+                .addVirtualTunnelInterface(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mSourceAddr),
+                        eq(mDestinationAddr),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testDeleteTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+
+        mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId);
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testTunnelInterfaceBinderDeath() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        refcountedRecord.binderDied();
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testAddRemoveAddressFromTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceAddAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+
+        mIpSecService.removeAddressFromTunnelInterface(
+                createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceDelAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
new file mode 100644
index 0000000..4a83d1b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.connectivity;
+
+import static android.Manifest.permission.CHANGE_NETWORK_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PermissionMonitorTest {
+    private static final int MOCK_UID = 10001;
+    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+
+    @Mock private Context mContext;
+    @Mock private PackageManager mPackageManager;
+
+    private PermissionMonitor mPermissionMonitor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
+        mPermissionMonitor = new PermissionMonitor(mContext, null);
+    }
+
+    private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
+        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
+        when(mPackageManager.getPackageInfo(MOCK_PACKAGE_NAMES[0], GET_PERMISSIONS))
+                .thenReturn(packageInfo);
+    }
+
+    private PackageInfo packageInfoWithPermissions(String[] permissions, boolean preinstalled) {
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.requestedPermissions = permissions;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.flags = preinstalled ? FLAG_SYSTEM : 0;
+        return packageInfo;
+    }
+
+    @Test
+    public void testHasPermission() {
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = packageInfoWithPermissions(new String[] {
+                CHANGE_NETWORK_STATE, NETWORK_STACK
+            }, false);
+        assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = packageInfoWithPermissions(new String[] {
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
+            }, false);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+    }
+
+    @Test
+    public void testIsPreinstalledSystemApp() {
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+        assertFalse(mPermissionMonitor.isPreinstalledSystemApp(app));
+
+        app = packageInfoWithPermissions(new String[] {}, true);
+        assertTrue(mPermissionMonitor.isPreinstalledSystemApp(app));
+    }
+
+    @Test
+    public void testHasUseBackgroundNetworksPermission() throws Exception {
+        expectPermission(new String[] { CHANGE_NETWORK_STATE }, false);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        // TODO : make this false when b/31479477 is fixed
+        expectPermission(new String[] {}, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        expectPermission(new String[] { CHANGE_WIFI_STATE }, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] {}, false);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    }
+}
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
index 3b0221c..79469e3 100644
--- a/tests/testables/tests/Android.mk
+++ b/tests/testables/tests/Android.mk
@@ -41,5 +41,7 @@
 
 LOCAL_CERTIFICATE := platform
 
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
 include $(BUILD_PACKAGE)
 
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index ab106d7..ebdcdfd 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -46,7 +46,8 @@
       message(that.message),
       fields(that.fields),
       primaryFields(that.primaryFields),
-      exclusiveField(that.exclusiveField) {}
+      exclusiveField(that.exclusiveField),
+      uidField(that.uidField) {}
 
 AtomDecl::AtomDecl(int c, const string& n, const string& m)
     :code(c),
@@ -262,6 +263,18 @@
             errorCount++;
         }
     }
+
+    if (field->options().GetExtension(os::statsd::is_uid) == true) {
+        if (javaType != JAVA_TYPE_INT) {
+            errorCount++;
+        }
+
+        if (atomDecl->uidField == 0) {
+            atomDecl->uidField = it->first;
+        } else {
+            errorCount++;
+        }
+    }
   }
 
   return errorCount;
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index edba3e2..5d2c302 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -84,6 +84,8 @@
     vector<int> primaryFields;
     int exclusiveField = 0;
 
+    int uidField = 0;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index d58c223..300c701 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -113,6 +113,98 @@
     fprintf(out, "// the single event tag id for all stats logs\n");
     fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
 
+    std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
+                                             "audio_state_changed",
+                                             "call_state_changed",
+                                             "phone_signal_strength_changed",
+                                             "mobile_bytes_transfer_by_fg_bg",
+                                             "mobile_bytes_transfer"};
+    fprintf(out,
+            "const std::set<int> "
+            "AtomsInfo::kNotTruncatingTimestampAtomWhiteList = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (kTruncatingAtomNames.find(atom->name) ==
+            kTruncatingAtomNames.end()) {
+            string constant = make_constant_name(atom->name);
+            fprintf(out, " %s,\n", constant.c_str());
+        }
+    }
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    fprintf(out,
+            "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        for (vector<AtomField>::const_iterator field = atom->fields.begin();
+             field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                string constant = make_constant_name(atom->name);
+                fprintf(out, " %s,\n", constant.c_str());
+                break;
+            }
+        }
+    }
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    fprintf(out,
+            "static std::map<int, int> "
+            "getAtomUidField() {\n");
+    fprintf(out, "  std::map<int, int> uidField;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->uidField == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding uid field for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+        fprintf(out, "    uidField[static_cast<int>(%s)] = %d;\n",
+                make_constant_name(atom->name).c_str(), atom->uidField);
+    }
+
+    fprintf(out, "    return uidField;\n");
+    fprintf(out, "};\n");
+
+    fprintf(out,
+            "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
+            "getAtomUidField();\n");
+
+    fprintf(out,
+            "static std::map<int, StateAtomFieldOptions> "
+            "getStateAtomFieldOptions() {\n");
+    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
+    fprintf(out, "    StateAtomFieldOptions opt;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding primary and exclusive fields for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+        fprintf(out, "    opt.primaryFields.clear();\n");
+        for (const auto& field : atom->primaryFields) {
+            fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
+        }
+
+        fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
+        fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
+                make_constant_name(atom->name).c_str());
+    }
+
+    fprintf(out, "    return options;\n");
+    fprintf(out, "  }\n");
+
+    fprintf(out,
+            "const std::map<int, StateAtomFieldOptions> "
+            "AtomsInfo::kStateAtomsFieldOptions = "
+            "getStateAtomFieldOptions();\n");
+
     // Print write methods
     fprintf(out, "\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -366,89 +458,26 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    std::set<string> kTruncatingAtomNames =
-        { "mobile_radio_power_state_changed", "audio_state_changed", "call_state_changed",
-          "phone_signal_strength_changed", "mobile_bytes_transfer_by_fg_bg",
-          "mobile_bytes_transfer"};
-    fprintf(out, "const static std::set<int> kNotTruncatingTimestampAtomWhiteList = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        if (kTruncatingAtomNames.find(atom->name) == kTruncatingAtomNames.end()) {
-            string constant = make_constant_name(atom->name);
-            fprintf(out, " %s,\n", constant.c_str());
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static std::set<int> kAtomsWithUidField = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-            if (field->name == "uid") {
-                string constant = make_constant_name(atom->name);
-                fprintf(out, " %s,\n", constant.c_str());
-                break;
-            }
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static std::set<int> kAtomsWithAttributionChain = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                string constant = make_constant_name(atom->name);
-                fprintf(out, " %s,\n", constant.c_str());
-                break;
-            }
-        }
-    }
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
-
     fprintf(out, "struct StateAtomFieldOptions {\n");
     fprintf(out, "  std::vector<int> primaryFields;\n");
     fprintf(out, "  int exclusiveField;\n");
+    fprintf(out, "};\n");
     fprintf(out, "\n");
+
+    fprintf(out, "struct AtomsInfo {\n");
     fprintf(out,
-            "  static std::map<int, StateAtomFieldOptions> "
-            "getStateAtomFieldOptions() {\n");
-    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
-    fprintf(out, "    StateAtomFieldOptions opt;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
-        if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
-            continue;
-        }
-        fprintf(out,
-                "\n    // Adding primary and exclusive fields for atom "
-                "(%d)%s\n",
-                atom->code, atom->name.c_str());
-        fprintf(out, "    opt.primaryFields.clear();\n");
-        for (const auto& field : atom->primaryFields) {
-            fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
-        }
-
-        fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
-        fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
-                make_constant_name(atom->name).c_str());
-    }
-
-    fprintf(out, "    return options;\n");
-    fprintf(out, "  }\n");
+            "  const static std::set<int> "
+            "kNotTruncatingTimestampAtomWhiteList;\n");
+    fprintf(out, "  const static std::map<int, int> kAtomsWithUidField;\n");
+    fprintf(out,
+            "  const static std::set<int> kAtomsWithAttributionChain;\n");
+    fprintf(out,
+            "  const static std::map<int, StateAtomFieldOptions> "
+            "kStateAtomsFieldOptions;\n");
     fprintf(out, "};\n");
 
-    fprintf(out,
-            "const static std::map<int, StateAtomFieldOptions> "
-            "kStateAtomsFieldOptions = "
-            "StateAtomFieldOptions::getStateAtomFieldOptions();\n");
+    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
+            maxPushedAtomId);
 
     // Print write methods
     fprintf(out, "//\n");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 309bc80..2f7b50d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,7 +28,6 @@
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.PasspointManagementObjectDefinition;
 import android.net.wifi.ScanResult;
-import android.net.wifi.ScanSettings;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -86,7 +85,7 @@
 
     boolean disableNetwork(int netId, String packageName);
 
-    void startScan(in ScanSettings requested, in WorkSource ws, String packageName);
+    void startScan(String packageName);
 
     List<ScanResult> getScanResults(String callingPackage);
 
diff --git a/wifi/java/android/net/wifi/ScanSettings.java b/wifi/java/android/net/wifi/ScanSettings.java
deleted file mode 100644
index 094ce34..0000000
--- a/wifi/java/android/net/wifi/ScanSettings.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Bundle of customized scan settings
- *
- * @see WifiManager#startCustomizedScan
- *
- * @hide
- */
-public class ScanSettings implements Parcelable {
-
-    /** channel set to scan. this can be null or empty, indicating a full scan */
-    public Collection<WifiChannel> channelSet;
-
-    /** public constructor */
-    public ScanSettings() { }
-
-    /** copy constructor */
-    public ScanSettings(ScanSettings source) {
-        if (source.channelSet != null)
-            channelSet = new ArrayList<WifiChannel>(source.channelSet);
-    }
-
-    /** check for validity */
-    public boolean isValid() {
-        for (WifiChannel channel : channelSet)
-            if (!channel.isValid()) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(channelSet == null ? 0 : channelSet.size());
-        if (channelSet != null)
-            for (WifiChannel channel : channelSet) channel.writeToParcel(out, flags);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<ScanSettings> CREATOR =
-            new Parcelable.Creator<ScanSettings>() {
-        @Override
-        public ScanSettings createFromParcel(Parcel in) {
-            ScanSettings settings = new ScanSettings();
-            int size = in.readInt();
-            if (size > 0) {
-                settings.channelSet = new ArrayList<WifiChannel>(size);
-                while (size-- > 0)
-                    settings.channelSet.add(WifiChannel.CREATOR.createFromParcel(in));
-            }
-            return settings;
-        }
-
-        @Override
-        public ScanSettings[] newArray(int size) {
-            return new ScanSettings[size];
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/WifiChannel.java b/wifi/java/android/net/wifi/WifiChannel.java
deleted file mode 100644
index 640481e..0000000
--- a/wifi/java/android/net/wifi/WifiChannel.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wifi Channel
- *
- * @see ScanSettings
- *
- * @hide
- */
-public class WifiChannel implements Parcelable {
-
-    private static final int MIN_FREQ_MHZ = 2412;
-    private static final int MAX_FREQ_MHZ = 5825;
-
-    private static final int MIN_CHANNEL_NUM = 1;
-    private static final int MAX_CHANNEL_NUM = 196;
-
-    /** frequency */
-    public int freqMHz;
-
-    /** channel number */
-    public int channelNum;
-
-    /** is it a DFS channel? */
-    public boolean isDFS;
-
-    /** public constructor */
-    public WifiChannel() { }
-
-    /** check for validity */
-    public boolean isValid() {
-        if (freqMHz < MIN_FREQ_MHZ || freqMHz > MAX_FREQ_MHZ) return false;
-        if (channelNum < MIN_CHANNEL_NUM || channelNum > MAX_CHANNEL_NUM) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(freqMHz);
-        out.writeInt(channelNum);
-        out.writeInt(isDFS ? 1 : 0);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<WifiChannel> CREATOR =
-            new Parcelable.Creator<WifiChannel>() {
-        @Override
-        public WifiChannel createFromParcel(Parcel in) {
-            WifiChannel channel = new WifiChannel();
-            channel.freqMHz = in.readInt();
-            channel.channelNum = in.readInt();
-            channel.isDFS = in.readInt() != 0;
-            return channel;
-        }
-
-        @Override
-        public WifiChannel[] newArray(int size) {
-            return new WifiChannel[size];
-        }
-    };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d2cdf8d..c8df087 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1634,7 +1634,7 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            mService.startScan(null, workSource, packageName);
+            mService.startScan(packageName);
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();