Merge "Reduce number of calls to getApplicationInfo"
diff --git a/Android.bp b/Android.bp
index e19ca84..ecdc082 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,8 +49,6 @@
         "rs/java/**/*.java",
 
         ":framework-javastream-protos",
-        // TODO: Resolve circular library dependency and remove media1-srcs
-        ":media1-srcs",
 
         "core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl",
         "core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl",
@@ -119,7 +117,6 @@
         "core/java/android/app/timedetector/ITimeDetectorService.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
-        "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
         "core/java/android/app/usage/IStorageStatsManager.aidl",
         "core/java/android/app/usage/IUsageStatsManager.aidl",
@@ -249,6 +246,7 @@
         "core/java/android/os/ICancellationSignal.aidl",
         "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
         "core/java/android/os/IDeviceIdleController.aidl",
+        "core/java/android/os/IDynamicAndroidService.aidl",
         "core/java/android/os/IHardwarePropertiesManager.aidl",
         ":libincident_aidl",
         "core/java/android/os/IMaintenanceActivityListener.aidl",
@@ -505,7 +503,11 @@
         "media/java/android/media/session/ICallback.aidl",
         "media/java/android/media/session/IOnMediaKeyListener.aidl",
         "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
+        "media/java/android/media/session/ISession.aidl",
         "media/java/android/media/session/ISession2TokensListener.aidl",
+        "media/java/android/media/session/ISessionCallback.aidl",
+        "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",
@@ -520,6 +522,8 @@
         "media/java/android/media/tv/ITvInputSessionCallback.aidl",
         "media/java/android/media/tv/ITvRemoteProvider.aidl",
         "media/java/android/media/tv/ITvRemoteServiceInput.aidl",
+        "media/java/android/service/media/IMediaBrowserService.aidl",
+        "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
         "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
         "telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
@@ -654,6 +658,7 @@
 
         ":storaged_aidl",
         ":vold_aidl",
+        ":gsiservice_aidl",
         ":installd_aidl",
         ":dumpstate_aidl",
         ":incidentcompanion_aidl",
@@ -713,6 +718,7 @@
             "frameworks/native/aidl/gui",
             "system/core/storaged/binder",
             "system/vold/binder",
+            "system/gsid/aidl",
             "system/bt/binder",
             "system/security/keystore/binder",
         ],
@@ -728,6 +734,7 @@
     no_framework_libs: true,
     libs: [
         "ext",
+        "updatable_media_stubs",
     ],
 
     jarjar_rules: "jarjar_rules_hidl.txt",
@@ -1289,7 +1296,7 @@
         ":non_openjdk_javadoc_files",
         ":android_icu4j_src_files_for_docs",
         ":conscrypt_public_api_files",
-        ":media-srcs-without-aidls",
+        ":updatable-media-srcs-without-aidls",
         "test-mock/src/**/*.java",
         "test-runner/src/**/*.java",
     ],
@@ -1351,7 +1358,7 @@
         ":non_openjdk_javadoc_files",
         ":android_icu4j_src_files_for_docs",
         ":conscrypt_public_api_files",
-        ":media-srcs-without-aidls",
+        ":updatable-media-srcs-without-aidls",
     ],
     srcs_lib: "framework",
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
diff --git a/Android.mk b/Android.mk
index e405345..65d4d24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
 update-api: doc-comment-check-docs
 
 # ==== hiddenapi lists =======================================
+ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
 .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
     PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
@@ -108,6 +109,7 @@
 
 $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
 $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+endif  # UNSAFE_DISABLE_HIDDENAPI_FLAGS
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 11c7599..9234849 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -39,7 +39,8 @@
     @Rule
     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    private final KernelCpuThreadReader mKernelCpuThreadReader = KernelCpuThreadReader.create();
+    private final KernelCpuThreadReader mKernelCpuThreadReader =
+            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000);
 
     @Test
     public void timeReadCurrentProcessCpuUsage() {
diff --git a/api/current.txt b/api/current.txt
index 3b6cfe3..ff93504 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1511,6 +1511,7 @@
     field public static final int use32bitAbi = 16844053; // 0x1010515
     field public static final int useAppZygote = 16844184; // 0x1010598
     field public static final int useDefaultMargins = 16843641; // 0x1010379
+    field public static final int useEmbeddedDex = 16844196; // 0x10105a4
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
     field public static final int userVisible = 16843409; // 0x1010291
@@ -1622,6 +1623,7 @@
     field @Deprecated public static final int yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
+    field public static final int zygotePreloadName = 16844195; // 0x10105a3
   }
 
   public static final class R.bool {
@@ -6176,6 +6178,7 @@
     method public void onTrimMemory(int);
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
+    method public final void startForeground(int, @NonNull android.app.Notification, int);
     method public final void stopForeground(boolean);
     method public final void stopForeground(int);
     method public final void stopSelf();
@@ -6483,6 +6486,10 @@
     method public void onColorsChanged(android.app.WallpaperColors, int);
   }
 
+  public interface ZygotePreload {
+    method public void doPreload(android.content.pm.ApplicationInfo);
+  }
+
 }
 
 package android.app.admin {
@@ -11233,6 +11240,7 @@
 
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
+    method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
     method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
@@ -11249,13 +11257,16 @@
     method public void pinShortcuts(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull android.os.UserHandle);
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
+    method public void registerPackageInstallerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method public void startPackageInstallerSessionDetailsActivity(android.content.pm.PackageInstaller.SessionInfo, android.graphics.Rect, android.os.Bundle);
     method public void startShortcut(@NonNull String, @NonNull String, @Nullable android.graphics.Rect, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
     method public void startShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.graphics.Rect, @Nullable android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
+    method public void unregisterPackageInstallerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     field public static final String ACTION_CONFIRM_PIN_APPWIDGET = "android.content.pm.action.CONFIRM_PIN_APPWIDGET";
     field public static final String ACTION_CONFIRM_PIN_SHORTCUT = "android.content.pm.action.CONFIRM_PIN_SHORTCUT";
     field public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
@@ -11313,7 +11324,7 @@
 
   public final class ModuleInfo implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getName();
+    method @Nullable public CharSequence getName();
     method @Nullable public String getPackageName();
     method public boolean isHidden();
     method public void writeToParcel(android.os.Parcel, int);
@@ -11445,6 +11456,7 @@
     method public long getSize();
     method public int getStagedSessionErrorCode();
     method public String getStagedSessionErrorMessage();
+    method public android.os.UserHandle getUser();
     method public boolean isActive();
     method public boolean isMultiPackage();
     method public boolean isSealed();
@@ -11919,13 +11931,13 @@
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
     field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
-    field public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5; // 0x5
-    field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4; // 0x4
-    field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2; // 0x2
-    field public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6; // 0x6
-    field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3; // 0x3
-    field public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1; // 0x1
-    field public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0; // 0x0
+    field public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
+    field public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
+    field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
+    field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
+    field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
+    field public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; // 0x0
+    field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 4; // 0x4
     field public int flags;
     field public String permission;
   }
@@ -13552,10 +13564,12 @@
     method public static android.graphics.Bitmap createScaledBitmap(@NonNull android.graphics.Bitmap, int, int, boolean);
     method public int describeContents();
     method public void eraseColor(@ColorInt int);
+    method public void eraseColor(@ColorLong long);
     method @CheckResult public android.graphics.Bitmap extractAlpha();
     method @CheckResult public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
     method public int getAllocationByteCount();
     method public int getByteCount();
+    method public android.graphics.Color getColor(int, int);
     method @Nullable public android.graphics.ColorSpace getColorSpace();
     method public android.graphics.Bitmap.Config getConfig();
     method public int getDensity();
@@ -13581,6 +13595,7 @@
     method public void reconfigure(int, int, android.graphics.Bitmap.Config);
     method public void recycle();
     method public boolean sameAs(android.graphics.Bitmap);
+    method public void setColorSpace(@NonNull android.graphics.ColorSpace);
     method public void setConfig(android.graphics.Bitmap.Config);
     method public void setDensity(int);
     method public void setHasAlpha(boolean);
@@ -14241,7 +14256,9 @@
 
   public class LinearGradient extends android.graphics.Shader {
     ctor public LinearGradient(float, float, float, float, @NonNull @ColorInt int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+    ctor public LinearGradient(float, float, float, float, @NonNull @ColorLong long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
     ctor public LinearGradient(float, float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
+    ctor public LinearGradient(float, float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
   }
 
   public class MaskFilter {
@@ -14382,6 +14399,7 @@
     method @Nullable public android.graphics.BlendMode getBlendMode();
     method @ColorInt public int getColor();
     method public android.graphics.ColorFilter getColorFilter();
+    method @ColorLong public long getColorLong();
     method public boolean getFillPath(android.graphics.Path, android.graphics.Path);
     method public int getFlags();
     method public String getFontFeatureSettings();
@@ -14402,6 +14420,7 @@
     method public float getRunAdvance(CharSequence, int, int, int, int, boolean, int);
     method public android.graphics.Shader getShader();
     method @ColorInt public int getShadowLayerColor();
+    method @ColorLong public long getShadowLayerColorLong();
     method public float getShadowLayerDx();
     method public float getShadowLayerDy();
     method public float getShadowLayerRadius();
@@ -14456,6 +14475,7 @@
     method public void setAntiAlias(boolean);
     method public void setBlendMode(@Nullable android.graphics.BlendMode);
     method public void setColor(@ColorInt int);
+    method public void setColor(@ColorLong long);
     method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
     method public void setDither(boolean);
     method public void setElegantTextHeight(boolean);
@@ -14472,6 +14492,7 @@
     method public android.graphics.PathEffect setPathEffect(android.graphics.PathEffect);
     method public android.graphics.Shader setShader(android.graphics.Shader);
     method public void setShadowLayer(float, float, float, @ColorInt int);
+    method public void setShadowLayer(float, float, float, @ColorLong long);
     method public void setStrikeThruText(boolean);
     method public void setStrokeCap(android.graphics.Paint.Cap);
     method public void setStrokeJoin(android.graphics.Paint.Join);
@@ -14766,7 +14787,9 @@
 
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, @NonNull @ColorInt int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+    ctor public RadialGradient(float, float, float, @NonNull @ColorLong long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
+    ctor public RadialGradient(float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
   }
 
   public final class RecordingCanvas extends android.graphics.Canvas {
@@ -15026,7 +15049,9 @@
 
   public class SweepGradient extends android.graphics.Shader {
     ctor public SweepGradient(float, float, @NonNull @ColorInt int[], @Nullable float[]);
+    ctor public SweepGradient(float, float, @NonNull @ColorLong long[], @Nullable float[]);
     ctor public SweepGradient(float, float, @ColorInt int, @ColorInt int);
+    ctor public SweepGradient(float, float, @ColorLong long, @ColorLong long);
   }
 
   public class Typeface {
@@ -24720,9 +24745,9 @@
     field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
     field public static final int KEY_TYPE_RELEASE = 3; // 0x3
     field public static final int KEY_TYPE_STREAMING = 1; // 0x1
-    field public static final int OFFLINE_LICENSE_INACTIVE = 2; // 0x2
+    field public static final int OFFLINE_LICENSE_STATE_RELEASED = 2; // 0x2
     field public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0; // 0x0
-    field public static final int OFFLINE_LICENSE_USABLE = 1; // 0x1
+    field public static final int OFFLINE_LICENSE_STATE_USABLE = 1; // 0x1
     field public static final String PROPERTY_ALGORITHMS = "algorithms";
     field public static final String PROPERTY_DESCRIPTION = "description";
     field public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
@@ -25483,7 +25508,7 @@
     method @Nullable public android.media.MediaTimestamp getTimestamp();
     method @NonNull public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
     method @NonNull public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(@NonNull android.media.DataSourceDesc);
-    method public android.media.VideoSize getVideoSize();
+    method public android.util.Size getVideoSize();
     method public boolean isLooping();
     method public Object loopCurrent(boolean);
     method public Object notifyWhenCommandLabelReached(@NonNull Object);
@@ -25575,6 +25600,14 @@
     field public static final int PLAYER_STATE_PAUSED = 1003; // 0x3eb
     field public static final int PLAYER_STATE_PLAYING = 1004; // 0x3ec
     field public static final int PLAYER_STATE_PREPARED = 1002; // 0x3ea
+    field public static final int PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR = 7; // 0x7
+    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_RESOURCE_BUSY = 5; // 0x5
+    field public static final int PREPARE_DRM_STATUS_RESTORE_ERROR = 6; // 0x6
+    field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
+    field public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4; // 0x4
     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
@@ -25617,7 +25650,7 @@
     method public void onMediaTimeDiscontinuity(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.MediaTimestamp);
     method public void onSubtitleData(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.SubtitleData);
     method public void onTimedMetaDataAvailable(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.TimedMetaData);
-    method public void onVideoSizeChanged(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.VideoSize);
+    method public void onVideoSizeChanged(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.util.Size);
   }
 
   public static final class MediaPlayer2.MetricsConstants {
@@ -26522,11 +26555,6 @@
     method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable java.util.Map<java.lang.String,java.lang.String>, @Nullable java.util.List<java.net.HttpCookie>);
   }
 
-  public final class VideoSize {
-    method public int getHeight();
-    method public int getWidth();
-  }
-
   public interface VolumeAutomation {
     method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
   }
@@ -27366,7 +27394,6 @@
     method public int getRatingType();
     method @Nullable public android.app.PendingIntent getSessionActivity();
     method @NonNull public android.media.session.MediaSession.Token getSessionToken();
-    method public String getTag();
     method @NonNull public android.media.session.MediaController.TransportControls getTransportControls();
     method public void registerCallback(@NonNull android.media.session.MediaController.Callback);
     method public void registerCallback(@NonNull android.media.session.MediaController.Callback, @Nullable android.os.Handler);
@@ -27497,7 +27524,6 @@
     method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
     method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo);
     method public void notifySession2Created(@NonNull android.media.Session2Token);
-    method public void notifySession2Destroyed(@NonNull android.media.Session2Token);
     method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
     method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
   }
@@ -29261,6 +29287,7 @@
     method public android.net.VpnService.Builder setBlocking(boolean);
     method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
     method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo);
+    method public android.net.VpnService.Builder setMetered(boolean);
     method public android.net.VpnService.Builder setMtu(int);
     method public android.net.VpnService.Builder setSession(String);
     method public android.net.VpnService.Builder setUnderlyingNetworks(android.net.Network[]);
@@ -38490,13 +38517,13 @@
     field public static final String BUCKET_ID = "bucket_id";
     field public static final String DATE_TAKEN = "datetaken";
     field public static final String DESCRIPTION = "description";
+    field public static final String GROUP_ID = "group_id";
     field public static final String IS_PRIVATE = "isprivate";
     field @Deprecated public static final String LATITUDE = "latitude";
     field @Deprecated public static final String LONGITUDE = "longitude";
     field @Deprecated public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final String ORIENTATION = "orientation";
     field @Deprecated public static final String PICASA_ID = "picasa_id";
-    field public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
   }
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -38550,6 +38577,8 @@
     field public static final String IS_TRASHED = "is_trashed";
     field public static final String MIME_TYPE = "mime_type";
     field public static final String OWNER_PACKAGE_NAME = "owner_package_name";
+    field public static final String PRIMARY_DIRECTORY = "primary_directory";
+    field public static final String SECONDARY_DIRECTORY = "secondary_directory";
     field public static final String SIZE = "_size";
     field public static final String TITLE = "title";
     field public static final String WIDTH = "width";
@@ -38617,13 +38646,13 @@
     field public static final String DATE_TAKEN = "datetaken";
     field public static final String DESCRIPTION = "description";
     field public static final String DURATION = "duration";
+    field public static final String GROUP_ID = "group_id";
     field public static final String IS_PRIVATE = "isprivate";
     field public static final String LANGUAGE = "language";
     field @Deprecated public static final String LATITUDE = "latitude";
     field @Deprecated public static final String LONGITUDE = "longitude";
     field @Deprecated public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final String RESOLUTION = "resolution";
-    field public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
     field public static final String TAGS = "tags";
   }
 
@@ -38847,6 +38876,7 @@
     field @Deprecated public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
     field @Deprecated public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
     field @Deprecated public static final int LOCATION_MODE_OFF = 0; // 0x0
+    field @Deprecated public static final int LOCATION_MODE_ON = 3; // 0x3
     field @Deprecated public static final int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
     field @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
     field @Deprecated public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
@@ -41871,10 +41901,6 @@
     method public void showSession(android.os.Bundle, int);
     field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final String SERVICE_META_DATA = "android.voice_interaction";
-    field public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1; // 0x1
-    field public static final int VOICE_STATE_FULFILLING = 3; // 0x3
-    field public static final int VOICE_STATE_LISTENING = 2; // 0x2
-    field public static final int VOICE_STATE_NONE = 0; // 0x0
   }
 
   public class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -49784,6 +49810,7 @@
     field public static final int EDGE_RIGHT = 8; // 0x8
     field public static final int EDGE_TOP = 1; // 0x1
     field public static final int FLAG_WINDOW_IS_OBSCURED = 1; // 0x1
+    field public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 2; // 0x2
     field public static final int INVALID_POINTER_ID = -1; // 0xffffffff
     field public static final int TOOL_TYPE_ERASER = 4; // 0x4
     field public static final int TOOL_TYPE_FINGER = 1; // 0x1
@@ -50336,6 +50363,7 @@
     method public final int getScrollX();
     method public final int getScrollY();
     method @android.view.ViewDebug.ExportedProperty(category="drawing") @ColorInt public int getSolidColor();
+    method @LayoutRes public int getSourceLayoutResId();
     method public android.animation.StateListAnimator getStateListAnimator();
     method protected int getSuggestedMinimumHeight();
     method protected int getSuggestedMinimumWidth();
@@ -51000,6 +51028,7 @@
   public class ViewConfiguration {
     ctor @Deprecated public ViewConfiguration();
     method public static android.view.ViewConfiguration get(android.content.Context);
+    method @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier();
     method public static long getDefaultActionModeHideDuration();
     method public static int getDoubleTapTimeout();
     method @Deprecated public static int getEdgeSlop();
@@ -56014,8 +56043,7 @@
     method public void show(@FloatRange(from=0) float, @FloatRange(from=0) float, float, float);
     method public void update();
     field public static final int SOURCE_BOUND_MAX_IN_SURFACE = 0; // 0x0
-    field public static final int SOURCE_BOUND_MAX_IN_VIEW = 1; // 0x1
-    field public static final int SOURCE_BOUND_MAX_VISIBLE = 2; // 0x2
+    field public static final int SOURCE_BOUND_MAX_VISIBLE = 1; // 0x1
   }
 
   public static class Magnifier.Builder {
diff --git a/api/system-current.txt b/api/system-current.txt
index d061d01..94dec1c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -122,6 +122,7 @@
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
+    field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
     field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
     field public static final String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
     field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
@@ -517,7 +518,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
-    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
+    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
     method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException;
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
@@ -1126,6 +1127,7 @@
     method public String getNotificationChannelId();
     method @Nullable public String getTaskRootClassName();
     method @Nullable public String getTaskRootPackageName();
+    method public boolean isInstantApp();
     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
@@ -1290,6 +1292,7 @@
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
+    field public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final String NETD_SERVICE = "netd";
@@ -1316,6 +1319,32 @@
     method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
   }
 
+  public class DynamicAndroidClient {
+    ctor public DynamicAndroidClient(@NonNull android.content.Context);
+    method public void bind();
+    method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener, @NonNull java.util.concurrent.Executor);
+    method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener);
+    method public void start(String, long);
+    method public void start(String, long, long);
+    method public void unbind();
+    field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
+    field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
+    field public static final int CAUSE_ERROR_IO = 3; // 0x3
+    field public static final int CAUSE_ERROR_IPC = 5; // 0x5
+    field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2
+    field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1
+    field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0
+    field public static final int STATUS_IN_PROGRESS = 2; // 0x2
+    field public static final int STATUS_IN_USE = 4; // 0x4
+    field public static final int STATUS_NOT_STARTED = 1; // 0x1
+    field public static final int STATUS_READY = 3; // 0x3
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static interface DynamicAndroidClient.OnStatusChangedListener {
+    method public void onStatusChanged(int, int, long);
+  }
+
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
     field public static final String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
     field public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
@@ -1637,6 +1666,7 @@
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int FLAG_REMOVED = 2; // 0x2
+    field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
     field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
@@ -1813,7 +1843,9 @@
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
     field public final float batteryLevel;
     field public final float brightness;
+    field public final long colorSampleDuration;
     field public final int colorTemperature;
+    field @Nullable public final long[] colorValueBuckets;
     field public final boolean isDefaultBrightnessConfig;
     field public final boolean isUserSetBrightness;
     field public final float lastBrightness;
@@ -3459,15 +3491,6 @@
     method public void stop();
   }
 
-  public final class Session2Token implements android.os.Parcelable {
-    ctor public Session2Token(@NonNull android.content.Context, @NonNull String, @Nullable android.os.Bundle);
-    method public void destroy();
-    method @NonNull public android.os.Bundle getExtras();
-    method public int getPid();
-    method public boolean isDestroyed();
-    field public static final String SESSION_SERVICE_INTERFACE = "android.media.MediaSession2Service";
-  }
-
   public static class SubtitleData.Builder {
     ctor public SubtitleData.Builder();
     ctor public SubtitleData.Builder(@NonNull android.media.SubtitleData);
@@ -3482,15 +3505,6 @@
     method @NonNull public android.media.TimedMetaData.Builder setTimedMetaData(long, @NonNull byte[]);
   }
 
-  public abstract class VolumeProvider {
-    method public void setCallback(android.media.VolumeProvider.Callback);
-  }
-
-  public abstract static class VolumeProvider.Callback {
-    ctor public VolumeProvider.Callback();
-    method public abstract void onVolumeChanged(android.media.VolumeProvider);
-  }
-
 }
 
 package android.media.audiopolicy {
@@ -3579,167 +3593,10 @@
 
 package android.media.session {
 
-  public final class ControllerCallbackLink implements android.os.Parcelable {
-    ctor public ControllerCallbackLink(@NonNull android.content.Context, @NonNull android.media.session.ControllerCallbackLink.CallbackStub);
-    ctor public ControllerCallbackLink(android.os.IBinder);
-    method public int describeContents();
-    method @NonNull public android.os.IBinder getBinder();
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyEvent(@NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyExtrasChanged(@Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyMetadataChanged(@Nullable android.media.MediaMetadata);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPlaybackStateChanged(@Nullable android.media.session.PlaybackState);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyQueueChanged(@Nullable java.util.List<android.media.session.MediaSession.QueueItem>);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyQueueTitleChanged(@Nullable CharSequence);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifySessionDestroyed();
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyVolumeInfoChanged(@NonNull android.media.session.MediaController.PlaybackInfo);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.session.ControllerCallbackLink> CREATOR;
-  }
-
-  public abstract static class ControllerCallbackLink.CallbackStub {
-    ctor public ControllerCallbackLink.CallbackStub();
-    method public void onEvent(@NonNull String, @Nullable android.os.Bundle);
-    method public void onExtrasChanged(@Nullable android.os.Bundle);
-    method public void onMetadataChanged(@Nullable android.media.MediaMetadata);
-    method public void onPlaybackStateChanged(@Nullable android.media.session.PlaybackState);
-    method public void onQueueChanged(@Nullable java.util.List<android.media.session.MediaSession.QueueItem>);
-    method public void onQueueTitleChanged(@Nullable CharSequence);
-    method public void onSessionDestroyed();
-    method public void onVolumeInfoChanged(@NonNull android.media.session.MediaController.PlaybackInfo);
-  }
-
-  public final class ControllerLink implements android.os.Parcelable {
-    ctor public ControllerLink(@NonNull android.media.session.ControllerLink.ControllerStub);
-    ctor public ControllerLink(android.os.IBinder);
-    method public int describeContents();
-    method @NonNull public android.os.IBinder getBinder();
-    method @Nullable public android.os.Bundle getExtras();
-    method @Nullable public android.media.MediaMetadata getMetadata();
-    method @Nullable public android.media.session.PlaybackState getPlaybackState();
-    method @Nullable public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
-    method @Nullable public CharSequence getQueueTitle();
-    method public int getRatingType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.session.ControllerLink> CREATOR;
-  }
-
-  public abstract static class ControllerLink.ControllerStub {
-    ctor public ControllerLink.ControllerStub();
-    method public void adjustVolume(@NonNull String, @NonNull String, @NonNull android.media.session.ControllerCallbackLink, int, int);
-    method public void fastForward(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method @Nullable public android.os.Bundle getExtras();
-    method public long getFlags();
-    method @Nullable public android.app.PendingIntent getLaunchPendingIntent();
-    method @Nullable public android.media.MediaMetadata getMetadata();
-    method @NonNull public String getPackageName();
-    method @Nullable public android.media.session.PlaybackState getPlaybackState();
-    method @Nullable public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
-    method @Nullable public CharSequence getQueueTitle();
-    method public int getRatingType();
-    method @NonNull public String getTag();
-    method @NonNull public android.media.session.MediaController.PlaybackInfo getVolumeAttributes();
-    method public void next(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void pause(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void play(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void playFromMediaId(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method public void playFromSearch(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method public void playFromUri(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.net.Uri, @Nullable android.os.Bundle);
-    method public void prepare(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void prepareFromMediaId(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method public void prepareFromSearch(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method public void prepareFromUri(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.net.Uri, @Nullable android.os.Bundle);
-    method public void previous(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void rate(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.media.Rating);
-    method public void registerCallback(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void rewind(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void seekTo(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, long);
-    method public void sendCommand(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.ResultReceiver);
-    method public void sendCustomAction(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method public boolean sendMediaButton(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.view.KeyEvent);
-    method public void setVolumeTo(@NonNull String, @NonNull String, @NonNull android.media.session.ControllerCallbackLink, int, int);
-    method public void skipToQueueItem(@NonNull String, @NonNull android.media.session.ControllerCallbackLink, long);
-    method public void stop(@NonNull String, @NonNull android.media.session.ControllerCallbackLink);
-    method public void unregisterCallback(@NonNull android.media.session.ControllerCallbackLink);
-  }
-
   public static final class MediaController.PlaybackInfo implements android.os.Parcelable {
     ctor public MediaController.PlaybackInfo(int, int, int, int, android.media.AudioAttributes);
   }
 
-  public abstract static class MediaSession.Callback {
-    method public void onSetMediaButtonEventDelegate(@NonNull android.media.session.MediaSessionEngine.MediaButtonEventDelegate);
-  }
-
-  public static final class MediaSession.Token implements android.os.Parcelable {
-    method public android.media.session.ControllerLink getControllerLink();
-  }
-
-  public final class MediaSessionEngine implements java.lang.AutoCloseable {
-    ctor public MediaSessionEngine(@NonNull android.content.Context, @NonNull android.media.session.SessionLink, @NonNull android.media.session.SessionCallbackLink, @NonNull android.media.session.MediaSessionEngine.CallbackStub);
-    method public void close();
-    method public String getCallingPackage();
-    method @NonNull public android.media.session.MediaController getController();
-    method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
-    method @NonNull public android.media.session.MediaSession.Token getSessionToken();
-    method public boolean isActive();
-    method public static boolean isActiveState(int);
-    method public void sendSessionEvent(@NonNull String, @Nullable android.os.Bundle);
-    method public void setActive(boolean);
-    method public void setCallback(@Nullable android.media.session.MediaSession.Callback);
-    method public void setCallback(@Nullable android.media.session.MediaSession.Callback, @NonNull android.os.Handler);
-    method public void setExtras(@Nullable android.os.Bundle);
-    method public void setFlags(int);
-    method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
-    method public void setMetadata(@Nullable android.media.MediaMetadata);
-    method public void setPlaybackState(@Nullable android.media.session.PlaybackState);
-    method public void setPlaybackToLocal(android.media.AudioAttributes);
-    method public void setPlaybackToRemote(@NonNull android.media.VolumeProvider);
-    method public void setQueue(@Nullable java.util.List<android.media.session.MediaSession.QueueItem>);
-    method public void setQueueTitle(@Nullable CharSequence);
-    method public void setRatingType(int);
-    method public void setSessionActivity(@Nullable android.app.PendingIntent);
-  }
-
-  public static final class MediaSessionEngine.CallbackStub {
-    ctor public MediaSessionEngine.CallbackStub();
-    method public void onAdjustVolume(String, int, int, android.media.session.ControllerCallbackLink, int);
-    method public void onCommand(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle, android.os.ResultReceiver);
-    method public void onCustomAction(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
-    method public void onFastForward(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onMediaButton(String, int, int, android.content.Intent, int, android.os.ResultReceiver);
-    method public void onMediaButtonFromController(String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
-    method public void onNext(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onPause(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onPlay(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onPlayFromMediaId(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
-    method public void onPlayFromSearch(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
-    method public void onPlayFromUri(String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
-    method public void onPrepare(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onPrepareFromMediaId(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
-    method public void onPrepareFromSearch(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
-    method public void onPrepareFromUri(String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
-    method public void onPrevious(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onRate(String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
-    method public void onRewind(String, int, int, android.media.session.ControllerCallbackLink);
-    method public void onSeekTo(String, int, int, android.media.session.ControllerCallbackLink, long);
-    method public void onSetVolumeTo(String, int, int, android.media.session.ControllerCallbackLink, int);
-    method public void onSkipToTrack(String, int, int, android.media.session.ControllerCallbackLink, long);
-    method public void onStop(String, int, int, android.media.session.ControllerCallbackLink);
-  }
-
-  public static interface MediaSessionEngine.MediaButtonEventDelegate {
-    method public boolean onMediaButtonIntent(android.content.Intent);
-  }
-
-  public static final class MediaSessionEngine.QueueItem {
-    ctor public MediaSessionEngine.QueueItem(android.media.MediaDescription, long);
-    ctor public MediaSessionEngine.QueueItem(android.os.Parcel);
-    method public android.media.MediaDescription getDescription();
-    method public long getQueueId();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int UNKNOWN_ID = -1; // 0xffffffff
-  }
-
   public final class MediaSessionManager {
     method @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, @Nullable android.os.Handler);
@@ -3753,66 +3610,6 @@
     method public void onVolumeKeyLongPress(android.view.KeyEvent);
   }
 
-  public final class SessionCallbackLink implements android.os.Parcelable {
-    ctor public SessionCallbackLink(android.os.IBinder);
-    method public int describeContents();
-    method @NonNull public android.os.IBinder getBinder();
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyAdjustVolume(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, int);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyCommand(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.ResultReceiver);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyCustomAction(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyFastForward(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyMediaButton(@NonNull String, int, int, @NonNull android.content.Intent, int, @Nullable android.os.ResultReceiver);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyMediaButtonFromController(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.content.Intent);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyNext(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPause(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPlay(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPlayFromMediaId(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPlayFromSearch(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPlayFromUri(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.net.Uri, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPrepare(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPrepareFromMediaId(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPrepareFromSearch(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull String, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPrepareFromUri(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.net.Uri, @Nullable android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyPrevious(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyRate(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, @NonNull android.media.Rating);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyRewind(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifySeekTo(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, long);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifySetVolumeTo(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, int);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifySkipToTrack(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink, long);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void notifyStop(@NonNull String, int, int, @NonNull android.media.session.ControllerCallbackLink);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.session.SessionCallbackLink> CREATOR;
-  }
-
-  public final class SessionLink implements android.os.Parcelable {
-    ctor public SessionLink(@NonNull android.media.session.SessionLink.SessionStub);
-    ctor public SessionLink(android.os.IBinder);
-    method public int describeContents();
-    method @NonNull public android.os.IBinder getBinder();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.session.SessionLink> CREATOR;
-  }
-
-  public abstract static class SessionLink.SessionStub {
-    ctor public SessionLink.SessionStub();
-    method public void destroySession();
-    method @NonNull public android.media.session.ControllerLink getController();
-    method public void sendEvent(@NonNull String, @Nullable android.os.Bundle);
-    method public void setActive(boolean);
-    method public void setCurrentVolume(int);
-    method public void setExtras(@Nullable android.os.Bundle);
-    method public void setFlags(int);
-    method public void setLaunchPendingIntent(@Nullable android.app.PendingIntent);
-    method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
-    method public void setMetadata(@Nullable android.media.MediaMetadata, long, @Nullable String);
-    method public void setPlaybackState(@Nullable android.media.session.PlaybackState);
-    method public void setPlaybackToLocal(@NonNull android.media.AudioAttributes);
-    method public void setPlaybackToRemote(int, int);
-    method public void setQueue(@Nullable java.util.List<android.media.session.MediaSession.QueueItem>);
-    method public void setQueueTitle(@Nullable CharSequence);
-    method public void setRatingType(int);
-  }
-
 }
 
 package android.media.soundtrigger {
@@ -4114,9 +3911,10 @@
   }
 
   public class ConnectivityManager {
-    method @RequiresPermission("android.permission.PACKET_KEEPALIVE_OFFLOAD") public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method public boolean getAvoidBadWifi();
     method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
+    method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -4127,6 +3925,9 @@
     field public static final int TETHERING_BLUETOOTH = 2; // 0x2
     field public static final int TETHERING_USB = 1; // 0x1
     field public static final int TETHERING_WIFI = 0; // 0x0
+    field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+    field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
   }
 
   public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -4135,6 +3936,11 @@
     method public void onTetheringStarted();
   }
 
+  public abstract static class ConnectivityManager.TetheringEntitlementValueListener {
+    ctor public ConnectivityManager.TetheringEntitlementValueListener();
+    method public void onEntitlementResult(int);
+  }
+
   public final class IpPrefix implements android.os.Parcelable {
     ctor public IpPrefix(java.net.InetAddress, int);
     ctor public IpPrefix(String);
@@ -4250,6 +4056,10 @@
     field public static final String EXTRA_PACKAGE_NAME = "packageName";
   }
 
+  public class NetworkStack {
+    field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
+  }
+
   public final class RouteInfo implements android.os.Parcelable {
     ctor public RouteInfo(android.net.IpPrefix, java.net.InetAddress, String, int);
     method public int getType();
@@ -5169,6 +4979,53 @@
     field public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
   }
 
+  public final class BatterySaverPolicyConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getAdjustBrightnessFactor();
+    method public boolean getAdvertiseIsEnabled();
+    method public boolean getDeferFullBackup();
+    method public boolean getDeferKeyValueBackup();
+    method @NonNull public java.util.Map<java.lang.String,java.lang.String> getDeviceSpecificSettings();
+    method public boolean getDisableAnimation();
+    method public boolean getDisableAod();
+    method public boolean getDisableLaunchBoost();
+    method public boolean getDisableOptionalSensors();
+    method public boolean getDisableSoundTrigger();
+    method public boolean getDisableVibration();
+    method public boolean getEnableAdjustBrightness();
+    method public boolean getEnableDataSaver();
+    method public boolean getEnableFirewall();
+    method public boolean getEnableQuickDoze();
+    method public boolean getForceAllAppsStandby();
+    method public boolean getForceBackgroundCheck();
+    method public int getGpsMode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.BatterySaverPolicyConfig> CREATOR;
+  }
+
+  public static final class BatterySaverPolicyConfig.Builder {
+    ctor public BatterySaverPolicyConfig.Builder();
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder addDeviceSpecificSetting(@NonNull String, @NonNull String);
+    method @NonNull public android.os.BatterySaverPolicyConfig build();
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setAdjustBrightnessFactor(float);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setAdvertiseIsEnabled(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDeferFullBackup(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDeferKeyValueBackup(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableAnimation(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableAod(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableLaunchBoost(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableOptionalSensors(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableSoundTrigger(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setDisableVibration(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableAdjustBrightness(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableDataSaver(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableFirewall(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setEnableQuickDoze(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceAllAppsStandby(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setForceBackgroundCheck(boolean);
+    method @NonNull public android.os.BatterySaverPolicyConfig.Builder setGpsMode(int);
+  }
+
   public class Binder implements android.os.IBinder {
     method public static final long clearCallingWorkSource();
     method public static final int getCallingWorkSourceUid();
@@ -5444,6 +5301,8 @@
   public final class PowerManager {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveMode();
+    method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSavings(boolean, int);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveMode(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
@@ -5582,6 +5441,7 @@
   }
 
   public final class UserHandle implements android.os.Parcelable {
+    method public static int getAppId(int);
     method public int getIdentifier();
     method @Deprecated public boolean isOwner();
     method public boolean isSystem();
@@ -5844,6 +5704,7 @@
   public static interface DeviceConfig.ActivityManager {
     field public static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
     field public static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
+    field public static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";
     field public static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
     field public static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
     field public static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
@@ -5859,9 +5720,9 @@
   }
 
   public static interface DeviceConfig.AttentionManagerService {
+    field public static final String COMPONENT_NAME = "component_name";
     field public static final String NAMESPACE = "attention_manager_service";
-    field public static final String PROPERTY_COMPONENT_NAME = "component_name";
-    field public static final String PROPERTY_SERVICE_ENABLED = "service_enabled";
+    field public static final String SERVICE_ENABLED = "service_enabled";
   }
 
   public static interface DeviceConfig.ContentCapture {
@@ -5875,9 +5736,9 @@
   }
 
   public static interface DeviceConfig.IntelligenceAttention {
+    field public static final String ATTENTION_ENABLED = "attention_enabled";
+    field public static final String ATTENTION_SETTINGS = "attention_settings";
     field public static final String NAMESPACE = "intelligence_attention";
-    field public static final String PROPERTY_ATTENTION_ENABLED = "attention_enabled";
-    field public static final String PROPERTY_ATTENTION_SETTINGS = "attention_settings";
   }
 
   public static interface DeviceConfig.NotificationAssistant {
@@ -5892,8 +5753,8 @@
 
   public static interface DeviceConfig.Privacy {
     field public static final String NAMESPACE = "privacy";
-    field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "enable_location_access_check";
-    field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "enable_permissions_hub";
+    field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
+    field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
   }
 
   public static interface DeviceConfig.RuntimeNative {
@@ -5907,8 +5768,9 @@
 
   public static interface DeviceConfig.Telephony {
     field public static final String NAMESPACE = "telephony";
-    field public static final String PROPERTY_ENABLE_RAMPING_RINGER = "enable_ramping_ringer";
-    field public static final String PROPERTY_RAMPING_RINGER_DURATION = "ramping_duration";
+    field public static final String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
+    field public static final String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
+    field public static final String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
   }
 
   public final class DocumentsContract {
@@ -7931,6 +7793,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
diff --git a/api/test-current.txt b/api/test-current.txt
index 76261dd..81eb1eb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -242,6 +242,7 @@
 
   public class NotificationManager {
     method public android.content.ComponentName getEffectsSuppressor();
+    method public boolean matchesCallFilter(android.os.Bundle);
   }
 
   public final class PictureInPictureParams implements android.os.Parcelable {
@@ -520,6 +521,7 @@
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
     field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
@@ -603,23 +605,10 @@
 
 package android.graphics {
 
-  public final class Bitmap implements android.os.Parcelable {
-    method public void eraseColor(@ColorLong long);
-    method public android.graphics.Color getColor(int, int);
-    method public void setColorSpace(@NonNull android.graphics.ColorSpace);
-  }
-
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
   }
 
-  public class Paint {
-    method @ColorLong public long getColorLong();
-    method @ColorLong public long getShadowLayerColorLong();
-    method public void setColor(@ColorLong long);
-    method public void setShadowLayer(float, float, float, @ColorLong long);
-  }
-
 }
 
 package android.graphics.drawable {
@@ -671,7 +660,9 @@
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
     field public final float batteryLevel;
     field public final float brightness;
+    field public final long colorSampleDuration;
     field public final int colorTemperature;
+    field @Nullable public final long[] colorValueBuckets;
     field public final boolean isDefaultBrightnessConfig;
     field public final boolean isUserSetBrightness;
     field public final float lastBrightness;
@@ -839,6 +830,17 @@
     method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
   }
 
+  public class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable {
+    method public android.media.MediaPlayer2.DrmInfo getDrmInfo(@NonNull android.media.DataSourceDesc);
+    method public android.media.MediaDrm.KeyRequest getDrmKeyRequest(@NonNull android.media.DataSourceDesc, @Nullable byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.Map<java.lang.String,java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
+    method public String getDrmPropertyString(@NonNull android.media.DataSourceDesc, @NonNull String) throws android.media.MediaPlayer2.NoDrmSchemeException;
+    method public Object prepareDrm(@NonNull android.media.DataSourceDesc, @NonNull java.util.UUID);
+    method public byte[] provideDrmKeyResponse(@NonNull android.media.DataSourceDesc, @Nullable byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
+    method public void releaseDrm(@NonNull android.media.DataSourceDesc) throws android.media.MediaPlayer2.NoDrmSchemeException;
+    method public void restoreDrmKeys(@NonNull android.media.DataSourceDesc, @NonNull byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
+    method public void setDrmPropertyString(@NonNull android.media.DataSourceDesc, @NonNull String, @NonNull String) throws android.media.MediaPlayer2.NoDrmSchemeException;
+  }
+
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -958,6 +960,10 @@
     method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
   }
 
+  public class NetworkStack {
+    field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
+  }
+
   public final class RouteInfo implements android.os.Parcelable {
     ctor public RouteInfo(android.net.IpPrefix, java.net.InetAddress, String, int);
     method public int getType();
@@ -1745,7 +1751,7 @@
 
   public static interface DeviceConfig.Privacy {
     field public static final String NAMESPACE = "privacy";
-    field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "enable_location_access_check";
+    field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
   }
 
   public final class MediaStore {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 30df850..9a6387a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -234,6 +234,8 @@
         BluetoothBondStateChanged bluetooth_bond_state_changed = 165;
         BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
         BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
+        ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
+        ProcessStartTime process_start_time = 169;
     }
 
     // Pulled events will start at field 10000.
@@ -288,6 +290,7 @@
         DebugElapsedClock debug_elapsed_clock = 10046;
         DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
         NumBiometricsEnrolled num_faces_enrolled = 10048;
+        RoleHolder role_holder = 10049;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3973,6 +3976,20 @@
     optional int32 num_enrolled = 2;
 }
 
+/**
+ * A mapping of role holder -> role
+ */
+message RoleHolder {
+    // uid of the role holder
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // package name of the role holder
+    optional string package_name = 2;
+
+    // the role held
+    optional string role = 3;
+}
+
 message AggStats {
     optional int64 min = 1;
 
@@ -4968,17 +4985,17 @@
  *     packages/apps/Nfc/src/com/android/nfc/cardemulation/AidRoutingManager.java
  */
 message NfcErrorOccurred {
-  enum Type {
-    UNKNOWN = 0;
-    CMD_TIMEOUT = 1;
-    ERROR_NOTIFICATION = 2;
-    AID_OVERFLOW = 3;
-  }
-  optional Type type = 1;
-  // If it's nci cmd timeout, log the timeout command.
-  optional uint32 nci_cmd = 2;
+    enum Type {
+        UNKNOWN = 0;
+        CMD_TIMEOUT = 1;
+        ERROR_NOTIFICATION = 2;
+        AID_OVERFLOW = 3;
+    }
+    optional Type type = 1;
+    // If it's nci cmd timeout, log the timeout command.
+    optional uint32 nci_cmd = 2;
 
-  optional uint32 error_ntf_status_code = 3;
+    optional uint32 error_ntf_status_code = 3;
 }
 
 /**
@@ -4987,14 +5004,14 @@
  *     packages/apps/Nfc/src/com/android/nfc/NfcService.java
  */
 message NfcStateChanged {
-  enum State {
-    UNKNOWN = 0;
-    OFF = 1;
-    ON = 2;
-    ON_LOCKED = 3; // Secure Nfc enabled.
-    CRASH_RESTART = 4; // NfcService watchdog timeout restart.
-  }
-  optional State state = 1;
+    enum State {
+        UNKNOWN = 0;
+        OFF = 1;
+        ON = 2;
+        ON_LOCKED = 3; // Secure Nfc enabled.
+        CRASH_RESTART = 4; // NfcService watchdog timeout restart.
+    }
+    optional State state = 1;
 }
 
 /**
@@ -5003,12 +5020,12 @@
  *     packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java
  */
 message NfcBeamOccurred {
-  enum Operation {
-      UNKNOWN = 0;
-      SEND = 1;
-      RECEIVE = 2;
-  }
-  optional Operation operation = 1;
+    enum Operation {
+        UNKNOWN = 0;
+        SEND = 1;
+        RECEIVE = 2;
+    }
+    optional Operation operation = 1;
 }
 
 /**
@@ -5018,16 +5035,16 @@
  *     packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
  */
 message NfcCardemulationOccurred {
-  enum Category {
-      UNKNOWN = 0;
-      HCE_PAYMENT = 1;
-      HCE_OTHER = 2;
-      OFFHOST = 3;
-  }
-  // Transaction belongs to HCE payment or HCE other category, or offhost.
-  optional Category category = 1;
-  // SeName from transaction: SIMx, eSEx, HCE, HCEF.
-  optional string se_name = 2;
+    enum Category {
+        UNKNOWN = 0;
+        HCE_PAYMENT = 1;
+        HCE_OTHER = 2;
+        OFFHOST = 3;
+    }
+    // Transaction belongs to HCE payment or HCE other category, or offhost.
+    optional Category category = 1;
+    // SeName from transaction: SIMx, eSEx, HCE, HCEF.
+    optional string se_name = 2;
 }
 
 /**
@@ -5036,16 +5053,16 @@
  *     packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java
  */
 message NfcTagOccurred {
-  enum Type {
-      UNKNOWN = 0;
-      URL = 1;
-      BT_PAIRING = 2;
-      PROVISION = 3;
-      WIFI_CONNECT = 4;
-      APP_LAUNCH = 5;
-      OTHERS = 6;
-  }
-  optional Type type = 1;
+    enum Type {
+        UNKNOWN = 0;
+        URL = 1;
+        BT_PAIRING = 2;
+        PROVISION = 3;
+        WIFI_CONNECT = 4;
+        APP_LAUNCH = 5;
+        OTHERS = 6;
+    }
+    optional Type type = 1;
 }
 
 /**
@@ -5065,18 +5082,18 @@
  *     packages/apps/SecureElement/src/com/android/se/Terminal.java
  */
 message SeStateChanged {
-  enum State {
-    UNKNOWN = 0;
-    INITIALIZED = 1;
-    DISCONNECTED = 2;
-    CONNECTED = 3;
-    HALCRASH = 4;
-  }
-  optional State state = 1;
+    enum State {
+        UNKNOWN = 0;
+        INITIALIZED = 1;
+        DISCONNECTED = 2;
+        CONNECTED = 3;
+        HALCRASH = 4;
+    }
+    optional State state = 1;
 
-  optional string state_change_reason = 2;
-  // SIMx or eSEx.
-  optional string terminal = 3;
+    optional string state_change_reason = 2;
+    // SIMx or eSEx.
+    optional string terminal = 3;
 }
 
 /**
@@ -5085,15 +5102,15 @@
  *     packages/apps/SecureElement/src/com/android/se/Terminal.java
  */
 message SeOmapiReported {
-  enum Operation {
-    UNKNOWN = 0;
-    OPEN_CHANNEL = 1;
-  }
-  optional Operation operation = 1;
-  // SIMx or eSEx.
-  optional string terminal = 2;
+    enum Operation {
+        UNKNOWN = 0;
+        OPEN_CHANNEL = 1;
+    }
+    optional Operation operation = 1;
+    // SIMx or eSEx.
+    optional string terminal = 2;
 
-  optional string package_name = 3;
+    optional string package_name = 3;
 }
 
 /**
@@ -5247,6 +5264,7 @@
         PERMISSION_IGNORED = 12;
         SWIPE_LEFT = 13;
         SWIPE_RIGHT = 14;
+        STACK_EXPANDED = 15;
     }
     optional Action action = 6;
 
@@ -5275,3 +5293,71 @@
     }
     optional State state = 4;
 }
+
+/**
+ * Logs PowerManagerService screen timeout resets (extensions) that happen when an attention check
+ * returns true.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
+ */
+message ScreenTimeoutExtensionReported {
+    // Describes how many times in a row did the power manager reset the screen off timeout.
+    optional uint32 consecutive_timeout_extended_count = 1;
+}
+
+/*
+* Logs number of milliseconds it takes to start a process.
+* The definition of app process start time is from the app launch time to
+* the time that Zygote finished forking the app process and loaded the
+* application package's java classes.
+
+* This metric is different from AppStartOccurred which is for foreground
+* activity only.
+
+* ProcessStartTime can report all processes (both foreground and background)
+* start time.
+*
+* Logged from:
+*   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+*/
+message ProcessStartTime {
+    // The uid of the ProcessRecord.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // The process pid.
+    optional int32 pid = 2;
+
+    // The process name.
+    // Usually package name, "system" for system server.
+    // Provided by ActivityManagerService.
+    optional string process_name = 3;
+
+    enum StartType {
+        UNKNOWN = 0;
+        WARM = 1;
+        HOT = 2;
+        COLD = 3;
+    }
+
+    // The start type.
+    optional StartType type = 4;
+
+    // The elapsed realtime at the start of the process.
+    optional int64 process_start_time_millis = 5;
+
+    // Number of milliseconds it takes to reach bind application.
+    optional int32 bind_application_delay_millis = 6;
+
+    // Number of milliseconds it takes to finish start of the process.
+    optional int32 process_start_delay_millis = 7;
+
+    // hostingType field in ProcessRecord, the component type such as "activity",
+    // "service", "content provider", "broadcast" or other strings.
+    optional string hosting_type = 8;
+
+    // hostingNameStr field in ProcessRecord. The component class name that runs
+    // in this process.
+    optional string hosting_name = 9;
+}
+
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index a6ba2ca..6f3eeaa 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -220,6 +220,9 @@
         // BuildInformation.
         {android::util::BUILD_INFORMATION,
          {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+        // RoleHolder.
+        {android::util::ROLE_HOLDER,
+         {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index d5c358d..24a9980 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -31,8 +31,8 @@
 StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type) {
     StatsdConfig config;
     config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
-    *config.add_atom_matcher() = temperatureAtomMatcher;
+    auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = atomMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
 
@@ -41,12 +41,12 @@
 
     auto gaugeMetric = config.add_gauge_metric();
     gaugeMetric->set_id(123456);
-    gaugeMetric->set_what(temperatureAtomMatcher.id());
+    gaugeMetric->set_what(atomMatcher.id());
     gaugeMetric->set_condition(screenIsOffPredicate.id());
     gaugeMetric->set_sampling_type(sampling_type);
     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
     *gaugeMetric->mutable_dimensions_in_what() =
-        CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
     gaugeMetric->set_bucket(FIVE_MINUTES);
     gaugeMetric->set_max_pull_delay_sec(INT_MAX);
     config.set_hash_strings_in_metric_report(false);
@@ -139,9 +139,9 @@
     EXPECT_GT((int)gaugeMetrics.data_size(), 1);
 
     auto data = gaugeMetrics.data(0);
-    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
     EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-    EXPECT_EQ(2 /* sensor name field */,
+    EXPECT_EQ(1 /* subsystem name field */,
               data.dimensions_in_what().value_tuple().dimensions_value(0).field());
     EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
     EXPECT_EQ(6, data.bucket_info_size());
@@ -152,8 +152,8 @@
     EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
     EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(1).atom_size());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
@@ -161,8 +161,8 @@
     EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(2).atom_size());
     EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -170,8 +170,8 @@
               data.bucket_info(2).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(3).atom_size());
     EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
@@ -179,8 +179,8 @@
               data.bucket_info(3).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(4).atom_size());
     EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
@@ -188,8 +188,8 @@
               data.bucket_info(4).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(5).atom_size());
     EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
@@ -197,8 +197,8 @@
               data.bucket_info(5).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0);
 }
 
 TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
@@ -262,9 +262,9 @@
     EXPECT_GT((int)gaugeMetrics.data_size(), 1);
 
     auto data = gaugeMetrics.data(0);
-    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
     EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-    EXPECT_EQ(2 /* sensor name field */,
+    EXPECT_EQ(1 /* subsystem name field */,
               data.dimensions_in_what().value_tuple().dimensions_value(0).field());
     EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
     EXPECT_EQ(3, data.bucket_info_size());
@@ -275,8 +275,8 @@
     EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
     EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(1).atom_size());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
@@ -284,8 +284,8 @@
     EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(2, data.bucket_info(2).atom_size());
     EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -295,10 +295,10 @@
               data.bucket_info(2).elapsed_timestamp_nanos(1));
     EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
-    EXPECT_TRUE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
+    EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0);
 }
 
 
@@ -366,9 +366,9 @@
     EXPECT_GT((int)gaugeMetrics.data_size(), 1);
 
     auto data = gaugeMetrics.data(0);
-    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
     EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-    EXPECT_EQ(2 /* sensor name field */,
+    EXPECT_EQ(1 /* subsystem name field */,
               data.dimensions_in_what().value_tuple().dimensions_value(0).field());
     EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
     EXPECT_EQ(3, data.bucket_info_size());
@@ -378,8 +378,8 @@
     EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(1).atom_size());
     EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
@@ -387,8 +387,8 @@
     EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
 
     EXPECT_EQ(1, data.bucket_info(2).atom_size());
     EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -396,9 +396,8 @@
               data.bucket_info(2).elapsed_timestamp_nanos(0));
     EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
-    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
-
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
 }
 
 #else
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 370c36c..aee0c1f 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -70,18 +70,19 @@
     StatsdConfig config;
     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
 
-    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
-    *config.add_atom_matcher() = temperatureAtomMatcher;
+    auto pulledAtomMatcher =
+            CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
 
     auto valueMetric = config.add_value_metric();
     valueMetric->set_id(123456);
-    valueMetric->set_what(temperatureAtomMatcher.id());
+    valueMetric->set_what(pulledAtomMatcher.id());
     *valueMetric->mutable_value_field() =
-            CreateDimensions(android::util::TEMPERATURE, {3 /* temperature degree field */});
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
     *valueMetric->mutable_dimensions_in_what() =
-            CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
     valueMetric->set_bucket(FIVE_MINUTES);
     valueMetric->set_min_bucket_size_nanos(minTime);
     valueMetric->set_use_absolute_value_on_reset(true);
@@ -92,17 +93,18 @@
     StatsdConfig config;
     config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
 
-    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
-    *config.add_atom_matcher() = temperatureAtomMatcher;
+    auto pulledAtomMatcher =
+                CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
 
     auto gaugeMetric = config.add_gauge_metric();
     gaugeMetric->set_id(123456);
-    gaugeMetric->set_what(temperatureAtomMatcher.id());
+    gaugeMetric->set_what(pulledAtomMatcher.id());
     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
     *gaugeMetric->mutable_dimensions_in_what() =
-            CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
     gaugeMetric->set_bucket(FIVE_MINUTES);
     gaugeMetric->set_min_bucket_size_nanos(minTime);
     return config;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index cab6eac..f3c4e12 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -31,8 +31,9 @@
 StatsdConfig CreateStatsdConfig() {
     StatsdConfig config;
     config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
-    *config.add_atom_matcher() = temperatureAtomMatcher;
+    auto pulledAtomMatcher =
+            CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
     *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
 
@@ -41,12 +42,12 @@
 
     auto valueMetric = config.add_value_metric();
     valueMetric->set_id(123456);
-    valueMetric->set_what(temperatureAtomMatcher.id());
+    valueMetric->set_what(pulledAtomMatcher.id());
     valueMetric->set_condition(screenIsOffPredicate.id());
     *valueMetric->mutable_value_field() =
-        CreateDimensions(android::util::TEMPERATURE, {3/* temperature degree field */ });
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
     *valueMetric->mutable_dimensions_in_what() =
-        CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+            CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
     valueMetric->set_bucket(FIVE_MINUTES);
     valueMetric->set_use_absolute_value_on_reset(true);
     valueMetric->set_skip_zero_diff_output(false);
@@ -134,9 +135,9 @@
     EXPECT_GT((int)valueMetrics.data_size(), 1);
 
     auto data = valueMetrics.data(0);
-    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
     EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-    EXPECT_EQ(2 /* sensor name field */,
+    EXPECT_EQ(1 /* subsystem name field */,
               data.dimensions_in_what().value_tuple().dimensions_value(0).field());
     EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
     EXPECT_EQ(5, data.bucket_info_size());
@@ -241,9 +242,9 @@
     EXPECT_GT((int)valueMetrics.data_size(), 1);
 
     auto data = valueMetrics.data(0);
-    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
     EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-    EXPECT_EQ(2 /* sensor name field */,
+    EXPECT_EQ(1 /* subsystem name field */,
               data.dimensions_in_what().value_tuple().dimensions_value(0).field());
     EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
     EXPECT_EQ(3, data.bucket_info_size());
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index c2e441b..7f4d1d0 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -19875,7 +19875,6 @@
 HSPLandroid/graphics/Bitmap;->createBitmap(Landroid/util/DisplayMetrics;IILandroid/graphics/Bitmap$Config;ZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;
 HSPLandroid/graphics/Bitmap;->createBitmap(Landroid/util/DisplayMetrics;[IIIIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
 HSPLandroid/graphics/Bitmap;->createBitmap([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
-HSPLandroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
 HSPLandroid/graphics/Bitmap;->createScaledBitmap(Landroid/graphics/Bitmap;IIZ)Landroid/graphics/Bitmap;
 HSPLandroid/graphics/Bitmap;->eraseColor(I)V
 HSPLandroid/graphics/Bitmap;->extractAlpha(Landroid/graphics/Paint;[I)Landroid/graphics/Bitmap;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e77e212..aac8f08 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1768,7 +1768,7 @@
     protected void onResume() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
         dispatchActivityResumed();
-        mActivityTransitionState.onResume(this, isTopOfTask());
+        mActivityTransitionState.onResume(this);
         enableAutofillCompatibilityIfNeeded();
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 75ea7bb..db3b720 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -42,6 +42,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -1777,6 +1778,10 @@
         private final int mSystemUiVisibility;
         private final boolean mIsTranslucent;
 
+        // TODO(b/116112787) TaskSnapshot must also book keep the color space from hardware bitmap
+        // when created.
+        private final ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+
         public TaskSnapshot(@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
                 int orientation, Rect contentInsets, boolean reducedResolution, float scale,
                 boolean isRealSnapshot, int windowingMode, int systemUiVisibility,
@@ -1822,6 +1827,13 @@
         }
 
         /**
+         * @return The color space of graphic buffer representing the screenshot.
+         */
+        public ColorSpace getColorSpace() {
+            return mColorSpace;
+        }
+
+        /**
          * @return The screen orientation the screenshot was taken in.
          */
         @UnsupportedAppUsage
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 86e658d..69c450c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,6 +23,7 @@
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
@@ -247,6 +248,9 @@
     /** Gets the task id for a given activity. */
     public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot);
 
+    /** Gets the basic info for a given activity. */
+    public abstract ActivityPresentationInfo getActivityPresentationInfo(@NonNull IBinder token);
+
     public abstract void setBooting(boolean booting);
     public abstract boolean isBooting();
     public abstract void setBooted(boolean booted);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 94b42ff..e2d868f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -33,6 +33,7 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IRemoteCallback;
@@ -916,7 +917,8 @@
                 // Unpackage the GraphicBuffer from the parceled thumbnail
                 final GraphicBuffer buffer = opts.getParcelable(KEY_ANIM_THUMBNAIL);
                 if (buffer != null) {
-                    mThumbnail = Bitmap.createHardwareBitmap(buffer);
+                    mThumbnail = Bitmap.wrapHardwareBuffer(
+                            HardwareBuffer.createFromGraphicBuffer(buffer), null);
                 }
                 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
                 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 03a09ee..21d66e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -129,6 +129,7 @@
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.SuperNotCalledException;
 import android.util.proto.ProtoOutputStream;
@@ -177,7 +178,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.text.DateFormat;
@@ -306,8 +306,14 @@
     @UnsupportedAppUsage
     final ArrayList<Application> mAllApplications
             = new ArrayList<Application>();
-    // set of instantiated backup agents, keyed by package name
-    final ArrayMap<String, BackupAgent> mBackupAgents = new ArrayMap<String, BackupAgent>();
+    /**
+     * Bookkeeping of instantiated backup agents indexed first by user id, then by package name.
+     * Indexing by user id supports parallel backups across users on system packages as they run in
+     * the same process with the same package name. Indexing by package name supports multiple
+     * distinct applications running in the same process.
+     */
+    private final SparseArray<ArrayMap<String, BackupAgent>> mBackupAgentsByUser =
+            new SparseArray<>();
     /** Reference to singleton {@link ActivityThread} */
     @UnsupportedAppUsage
     private static volatile ActivityThread sCurrentActivityThread;
@@ -660,10 +666,11 @@
         ApplicationInfo appInfo;
         CompatibilityInfo compatInfo;
         int backupMode;
+        int userId;
         public String toString() {
             return "CreateBackupAgentData{appInfo=" + appInfo
                     + " backupAgent=" + appInfo.backupAgentName
-                    + " mode=" + backupMode + "}";
+                    + " mode=" + backupMode + " userId=" + userId + "}";
         }
     }
 
@@ -878,20 +885,22 @@
         }
 
         public final void scheduleCreateBackupAgent(ApplicationInfo app,
-                CompatibilityInfo compatInfo, int backupMode) {
+                CompatibilityInfo compatInfo, int backupMode, int userId) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
             d.compatInfo = compatInfo;
             d.backupMode = backupMode;
+            d.userId = userId;
 
             sendMessage(H.CREATE_BACKUP_AGENT, d);
         }
 
         public final void scheduleDestroyBackupAgent(ApplicationInfo app,
-                CompatibilityInfo compatInfo) {
+                CompatibilityInfo compatInfo, int userId) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
             d.compatInfo = compatInfo;
+            d.userId = userId;
 
             sendMessage(H.DESTROY_BACKUP_AGENT, d);
         }
@@ -3617,7 +3626,8 @@
 
         try {
             IBinder binder = null;
-            BackupAgent agent = mBackupAgents.get(packageName);
+            ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId);
+            BackupAgent agent = backupAgents.get(packageName);
             if (agent != null) {
                 // reusing the existing instance
                 if (DEBUG_BACKUP) {
@@ -3636,9 +3646,9 @@
                     context.setOuterContext(agent);
                     agent.attach(context);
 
-                    agent.onCreate();
+                    agent.onCreate(UserHandle.of(data.userId));
                     binder = agent.onBind();
-                    mBackupAgents.put(packageName, agent);
+                    backupAgents.put(packageName, agent);
                 } catch (Exception e) {
                     // If this is during restore, fail silently; otherwise go
                     // ahead and let the user see the crash.
@@ -3654,7 +3664,7 @@
 
             // tell the OS that we're live now
             try {
-                ActivityManager.getService().backupAgentCreated(packageName, binder);
+                ActivityManager.getService().backupAgentCreated(packageName, binder, data.userId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -3670,7 +3680,8 @@
 
         LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
-        BackupAgent agent = mBackupAgents.get(packageName);
+        ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId);
+        BackupAgent agent = backupAgents.get(packageName);
         if (agent != null) {
             try {
                 agent.onDestroy();
@@ -3678,12 +3689,21 @@
                 Slog.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo);
                 e.printStackTrace();
             }
-            mBackupAgents.remove(packageName);
+            backupAgents.remove(packageName);
         } else {
             Slog.w(TAG, "Attempt to destroy unknown backup agent " + data);
         }
     }
 
+    private ArrayMap<String, BackupAgent> getBackupAgentsForUser(int userId) {
+        ArrayMap<String, BackupAgent> backupAgents = mBackupAgentsByUser.get(userId);
+        if (backupAgents == null) {
+            backupAgents = new ArrayMap<>();
+            mBackupAgentsByUser.put(userId, backupAgents);
+        }
+        return backupAgents;
+    }
+
     @UnsupportedAppUsage
     private void handleCreateService(CreateServiceData data) {
         // If we are getting ready to gc after going to the background, well
@@ -5936,18 +5956,6 @@
         StrictMode.initThreadDefaults(data.appInfo);
         StrictMode.initVmDefaults(data.appInfo);
 
-        // We deprecated Build.SERIAL and only apps that target pre NMR1
-        // SDK can see it. Since access to the serial is now behind a
-        // permission we push down the value and here we fix it up
-        // before any app code has been loaded.
-        try {
-            Field field = Build.class.getDeclaredField("SERIAL");
-            field.setAccessible(true);
-            field.set(Build.class, data.buildSerial);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            /* ignore */
-        }
-
         if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
@@ -6083,7 +6091,12 @@
             instrApp.initForUser(UserHandle.myUserId());
             final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true, false);
-            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+
+            // The test context's op package name == the target app's op package name, because
+            // the app ops manager checks the op package name against the real calling UID,
+            // which is what the target package name is associated with.
+            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
+                    appContext.getOpPackageName());
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 3201feb..3a95839 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -258,10 +258,10 @@
         }
     }
 
-    public void onResume(Activity activity, boolean isTopOfTask) {
+    public void onResume(Activity activity) {
         // After orientation change, the onResume can come in before the top Activity has
         // left, so if the Activity is not top, wait a second for the top Activity to exit.
-        if (isTopOfTask || mEnterTransitionCoordinator == null) {
+        if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
             restoreExitedViews();
             restoreReenteringViews();
         } else {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index eae7d6b..5814e69 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -363,7 +363,7 @@
         final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
 
         mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
-                .setContainerLayer()
+                .setContainerLayer(true)
                 .setParent(mSurfaceView.getSurfaceControl())
                 .setName(DISPLAY_NAME)
                 .build();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a937422..98032dc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3017,6 +3017,15 @@
     }
 
     @Override
+    public String getAppPredictionServicePackageName() {
+        try {
+            return mPM.getAppPredictionServicePackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public String getContentCaptureServicePackageName() {
         try {
             return mPM.getContentCaptureServicePackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 92cdb20..1a728c1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2128,7 +2128,7 @@
                 flags | CONTEXT_REGISTER_PACKAGE);
         if (pi != null) {
             ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
-                    new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
+                    new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
 
             final int displayId = getDisplayId();
 
@@ -2156,14 +2156,14 @@
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
             return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
-                    flags, null);
+                    flags, null, null);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
             ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
-                    flags, null);
+                    flags, null, null);
 
             final int displayId = getDisplayId();
 
@@ -2190,7 +2190,7 @@
         final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
         final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
-                mActivityToken, mUser, mFlags, classLoader);
+                mActivityToken, mUser, mFlags, classLoader, null);
 
         final int displayId = getDisplayId();
 
@@ -2214,7 +2214,7 @@
         }
 
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
-                mActivityToken, mUser, mFlags, mClassLoader);
+                mActivityToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
@@ -2229,7 +2229,7 @@
         }
 
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
-                mActivityToken, mUser, mFlags, mClassLoader);
+                mActivityToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = display.getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
@@ -2243,7 +2243,7 @@
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
         return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
-                flags, mClassLoader);
+                flags, mClassLoader, null);
     }
 
     @Override
@@ -2251,7 +2251,7 @@
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
         return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
-                flags, mClassLoader);
+                flags, mClassLoader, null);
     }
 
     @Override
@@ -2397,7 +2397,7 @@
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
-                null);
+                null, null);
         context.setResources(packageInfo.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
@@ -2414,7 +2414,7 @@
     static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
         final LoadedApk packageInfo = systemContext.mPackageInfo;
         ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
-                null, null, 0, null);
+                null, null, 0, null, null);
         context.setResources(createResources(null, packageInfo, null, displayId, null,
                 packageInfo.getCompatibilityInfo()));
         context.updateDisplay(displayId);
@@ -2431,9 +2431,14 @@
 
     @UnsupportedAppUsage
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
+        return createAppContext(mainThread, packageInfo, null);
+    }
+
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
+            String opPackageName) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
-                null);
+                null, opPackageName);
         context.setResources(packageInfo.getResources());
         return context;
     }
@@ -2461,7 +2466,7 @@
         }
 
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
-                activityToken, null, 0, classLoader);
+                activityToken, null, 0, classLoader, null);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2491,7 +2496,7 @@
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
             @NonNull LoadedApk packageInfo, @Nullable String splitName,
             @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
-            @Nullable ClassLoader classLoader) {
+            @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
         mOuterContext = this;
 
         // If creator didn't specify which storage to use, use the default
@@ -2520,9 +2525,11 @@
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
 
+        String opPackageName;
+
         if (container != null) {
             mBasePackageName = container.mBasePackageName;
-            mOpPackageName = container.mOpPackageName;
+            opPackageName = container.mOpPackageName;
             setResources(container.mResources);
             mDisplay = container.mDisplay;
         } else {
@@ -2533,12 +2540,14 @@
                 // processes.  For purposes of app ops, we must then consider the context as
                 // belonging to the package of this process, not the system itself, otherwise
                 // the package+uid verifications in app ops will fail.
-                mOpPackageName = ActivityThread.currentPackageName();
+                opPackageName = ActivityThread.currentPackageName();
             } else {
-                mOpPackageName = mBasePackageName;
+                opPackageName = mBasePackageName;
             }
         }
 
+        mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
+
         mContentResolver = new ApplicationContentResolver(this, mainThread);
     }
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 412d7f4..3aa9fa7 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -203,7 +203,7 @@
     void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
     void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason);
     void setServiceForeground(in ComponentName className, in IBinder token,
-            int id, in Notification notification, int flags);
+            int id, in Notification notification, int flags, int foregroundServiceType);
     boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
     void getMemoryInfo(out ActivityManager.MemoryInfo outInfo);
     List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
@@ -221,8 +221,8 @@
     boolean shutdown(int timeout);
     void stopAppSwitches();
     void resumeAppSwitches();
-    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int userId);
-    void backupAgentCreated(in String packageName, in IBinder agent);
+    boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId);
+    void backupAgentCreated(in String packageName, in IBinder agent, int userId);
     void unbindBackupAgent(in ApplicationInfo appInfo);
     int getUidForIntentSender(in IIntentSender sender);
     int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index c64fcf3..e7a8c0e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -88,9 +88,9 @@
     void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
     void setSchedulingGroup(int group);
     void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
-            int backupMode);
+            int backupMode, int userId);
     void scheduleDestroyBackupAgent(in ApplicationInfo app,
-            in CompatibilityInfo compatInfo);
+            in CompatibilityInfo compatInfo, int userId);
     void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7c550d4..028e3ef 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8122,6 +8122,10 @@
                     big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
             }
+            bindMediaActionButton(big, R.id.media_seamless, new Action(R.drawable.ic_media_seamless,
+                    mBuilder.mContext.getString(
+                            com.android.internal.R.string.ext_media_seamless_action), null), p);
+            big.setViewVisibility(R.id.media_seamless, View.GONE);
             handleImage(big);
             return big;
         }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 6a1ff29..8207e0a 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -828,6 +828,7 @@
     /**
      * @hide
      */
+    @TestApi
     public boolean matchesCallFilter(Bundle extras) {
         INotificationManager service = getService();
         try {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 5fa8526..87bf5ed 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -16,7 +16,10 @@
 
 package android.app;
 
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentCallbacks2;
@@ -685,12 +688,10 @@
      * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
      * this API.</p>
      *
-     * <p>To use this API, apps targeting API {@link android.os.Build.VERSION_CODES#Q} or later must
-     * specify the foreground service type using attribute
-     * {@link android.R.attr#foregroundServiceType} in service element of manifest file, otherwise
-     * a SecurityException is thrown when this API is called. Apps targeting API older than
-     * {@link android.os.Build.VERSION_CODES#Q} do not need to specify the foreground service type
-     * </p>
+     * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify
+     * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in
+     * service element of manifest file. The value of attribute
+     * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
      *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
@@ -703,7 +704,42 @@
         try {
             mActivityManager.setServiceForeground(
                     new ComponentName(this, mClassName), mToken, id,
-                    notification, 0);
+                    notification, 0, FOREGROUND_SERVICE_TYPE_MANIFEST);
+        } catch (RemoteException ex) {
+        }
+    }
+
+  /**
+   * An overloaded version of {@link #startForeground(int, Notification)} with additional
+   * foregroundServiceType parameter.
+   *
+   * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify
+   * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in
+   * service element of manifest file. The value of attribute
+   * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
+   *
+   * <p>The foregroundServiceType parameter must be a subset flags of what is specified in manifest
+   * attribute {@link android.R.attr#foregroundServiceType}, if not, an IllegalArgumentException is
+   * thrown. Specify foregroundServiceType parameter as
+   * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that
+   * is specified in manifest attribute foregroundServiceType.</p>
+   *
+   * @param id The identifier for this notification as per
+   * {@link NotificationManager#notify(int, Notification)
+   * NotificationManager.notify(int, Notification)}; must not be 0.
+   * @param notification The Notification to be displayed.
+   * @param foregroundServiceType must be a subset flags of manifest attribute
+   * {@link android.R.attr#foregroundServiceType} flags.
+   * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest
+   *     attribute {@link android.R.attr#foregroundServiceType}.
+   * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags.
+   */
+    public final void startForeground(int id, @NonNull Notification notification,
+            int foregroundServiceType) {
+        try {
+            mActivityManager.setServiceForeground(
+                    new ComponentName(this, mClassName), mToken, id,
+                    notification, 0, foregroundServiceType);
         } catch (RemoteException ex) {
         }
     }
@@ -731,7 +767,8 @@
     public final void stopForeground(@StopForegroundFlags int flags) {
         try {
             mActivityManager.setServiceForeground(
-                    new ComponentName(this, mClassName), mToken, 0, null, flags);
+                    new ComponentName(this, mClassName), mToken, 0, null,
+                    flags, 0);
         } catch (RemoteException ex) {
         }
     }
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index 80fb805..9fabfde 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -23,6 +23,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.transition.TransitionUtils;
@@ -234,7 +235,8 @@
                 return null;
             }
             if (bitmap == null) {
-                bitmap = Bitmap.createHardwareBitmap(buffer);
+                bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer),
+                                                   null);
             }
             ImageView imageView = new ImageView(context);
             view = imageView;
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 9b66c92..3119b37 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -289,20 +289,23 @@
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it removes any associated pending intent for this client.
+     * @return A list of configs that are currently active for this client. If the pendingIntent is
+     *         null, this will be an empty list.
      * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
      */
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
-    public void setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent)
+    public long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent)
             throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (pendingIntent == null) {
                     service.removeActiveConfigsChangedOperation(mContext.getOpPackageName());
+                    return new long[0];
                 } else {
                     // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
                     IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    service.setActiveConfigsChangedOperation(intentSender,
+                    return service.setActiveConfigsChangedOperation(intentSender,
                             mContext.getOpPackageName());
                 }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ee13164..807b7f2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -30,7 +30,6 @@
 import android.app.slice.SliceManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timezone.RulesManager;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.IUsageStatsManager;
@@ -132,11 +131,13 @@
 import android.os.Build;
 import android.os.DeviceIdleManager;
 import android.os.DropBoxManager;
+import android.os.DynamicAndroidManager;
 import android.os.HardwarePropertiesManager;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.IDumpstate;
+import android.os.IDynamicAndroidService;
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
@@ -1221,13 +1222,6 @@
                             throws ServiceNotFoundException {
                         return new TimeDetector();
                     }});
-        registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
-                new CachedServiceFetcher<TimeZoneDetector>() {
-                    @Override
-                    public TimeZoneDetector createService(ContextImpl ctx)
-                            throws ServiceNotFoundException {
-                        return new TimeZoneDetector();
-                    }});
 
         registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
                 new CachedServiceFetcher<PermissionManager>() {
@@ -1260,6 +1254,17 @@
                         return new RollbackManager(ctx.getOuterContext(),
                                 IRollbackManager.Stub.asInterface(b));
                     }});
+
+        registerService(Context.DYNAMIC_ANDROID_SERVICE, DynamicAndroidManager.class,
+                new CachedServiceFetcher<DynamicAndroidManager>() {
+                    @Override
+                    public DynamicAndroidManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.DYNAMIC_ANDROID_SERVICE);
+                        return new DynamicAndroidManager(
+                                IDynamicAndroidService.Stub.asInterface(b));
+                    }});
         //CHECKSTYLE:ON IndentationCheck
     }
 
diff --git a/core/java/android/app/ZygotePreload.java b/core/java/android/app/ZygotePreload.java
new file mode 100644
index 0000000..a295af3
--- /dev/null
+++ b/core/java/android/app/ZygotePreload.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open 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;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * This is the interface to be implemented for the class that is specified by the
+ * {@link android.R.styleable#AndroidManifestApplication_zygotePreloadName
+ * android:zygotePreloadName} of the &lt;application&gt; tag.
+ *
+ * It is responsible for preloading application code and data, that will be shared by all
+ * isolated services that have the
+ * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} attribute
+ * of the &lt;service&gt; tag set to <code>true</code>.
+ *
+ * Note that this implementations of this class must provide a default constructor with no
+ * arguments.
+ */
+public interface ZygotePreload {
+    /**
+     * This method is called once every time the Application Zygote is started. It is normally
+     * started the first time an isolated service that uses it is started. The Application Zygote
+     * will be stopped when all isolated services that use it are stopped.
+     *
+     * @param appInfo The ApplicationInfo object belonging to the application
+     */
+    void doPreload(ApplicationInfo appInfo);
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f0d0aad..428c9b0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3149,6 +3149,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
      *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setPasswordHistoryLength(@NonNull ComponentName admin, int length) {
         if (mService != null) {
             try {
@@ -10590,14 +10591,19 @@
     }
 
     /**
-     * Returns if a package is whitelisted to access cross-profile calendar APIs.
+     * Returns if a package is allowed to access cross-profile calendar APIs.
+     *
+     * <p>A package is allowed to access cross-profile calendar APIs if it's allowed by
+     * admins via {@link #setCrossProfileCalendarPackages(ComponentName, Set)} and
+     * {@link android.provider.Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED}
+     * is turned on in the managed profile.
      *
      * <p>To query for a specific user, use
      * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
      * that user, and get a {@link DevicePolicyManager} from this context.
      *
      * @param packageName the name of the package
-     * @return {@code true} if the package is whitelisted to access cross-profile calendar APIs.
+     * @return {@code true} if the package is allowed to access cross-profile calendar APIs.
      * {@code false} otherwise.
      *
      * @see #setCrossProfileCalendarPackages(ComponentName, Set)
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
deleted file mode 100644
index ef2cbab..0000000
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.timezonedetector;
-
-/**
- * System private API to comunicate with time zone detector service.
- *
- * <p>Used by parts of the Android system with signals associated with the device's time zone to
- * provide information to the Time Zone Detector Service.
- *
- * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
- * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
- * for more complete documentation.
- *
- *
- * {@hide}
- */
-interface ITimeZoneDetectorService {
-  void stubbedCall();
-}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
deleted file mode 100644
index be3c764..0000000
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.timezonedetector;
-
-import android.annotation.SystemService;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-
-/**
- * The interface through which system components can send signals to the TimeZoneDetectorService.
- * @hide
- */
-@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
-public final class TimeZoneDetector {
-
-    private static final String TAG = "timezonedetector.TimeZoneDetector";
-    private static final boolean DEBUG = false;
-
-    private final ITimeZoneDetectorService mITimeZoneDetectorService;
-
-    public TimeZoneDetector() throws ServiceNotFoundException {
-        mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
-
-    }
-    /**
-     * Does nothing.
-     * TODO: Remove this when the service implementation is built out.
-     */
-    public void stubbedCall() {
-        if (DEBUG) {
-            Log.d(TAG, "stubbedCall called");
-        }
-        try {
-            mITimeZoneDetectorService.stubbedCall();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 451f44b..8cbe7a3 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -405,6 +405,16 @@
         }
 
         /**
+         * Indicates whether it is an instant app.
+         * STOPSHIP b/111407095: Add GTS tests for the newly added API method.
+         * @hide
+         */
+        @SystemApi
+        public boolean isInstantApp() {
+            return (mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == FLAG_IS_PACKAGE_INSTANT_APP;
+        }
+
+        /**
          * The class name of the source of this event. This may be null for
          * certain events.
          */
@@ -530,7 +540,7 @@
 
         /** @hide */
         public Event getObfuscatedIfInstantApp() {
-            if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
+            if (!isInstantApp()) {
                 return this;
             }
             final Event ret = new Event(this);
@@ -735,6 +745,7 @@
                 p.writeString(event.mNotificationChannelId);
                 break;
         }
+        p.writeInt(event.mFlags);
     }
 
     /**
@@ -802,6 +813,7 @@
                 eventOut.mNotificationChannelId = p.readString();
                 break;
         }
+        eventOut.mFlags = p.readInt();
     }
 
     @Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index adc1bf2..957a484 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3186,7 +3186,6 @@
             CROSS_PROFILE_APPS_SERVICE,
             //@hide: SYSTEM_UPDATE_SERVICE,
             //@hide: TIME_DETECTOR_SERVICE,
-            //@hide: TIME_ZONE_DETECTOR_SERVICE,
             PERMISSION_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -4564,15 +4563,6 @@
     public static final String TIME_DETECTOR_SERVICE = "time_detector";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve an
-     * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
-     * @hide
-     *
-     * @see #getSystemService(String)
-     */
-    public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
-
-    /**
      * Binder service name for {@link AppBindingService}.
      * @hide
      */
@@ -4585,6 +4575,14 @@
      */
     public static final String TELEPHONY_RCS_SERVICE = "ircs";
 
+     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.os.DynamicAndroidManager}.
+     * @hide
+     */
+    @SystemApi
+    public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";
+
     /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
diff --git a/core/java/android/content/DynamicAndroidClient.java b/core/java/android/content/DynamicAndroidClient.java
new file mode 100644
index 0000000..571cba4
--- /dev/null
+++ b/core/java/android/content/DynamicAndroidClient.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.content;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * This class contains methods and constants used to start DynamicAndroid
+ * installation, and a listener for progress update.
+ * @hide
+ */
+@SystemApi
+public class DynamicAndroidClient {
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_NOT_STARTED,
+            STATUS_IN_PROGRESS,
+            STATUS_READY,
+            STATUS_IN_USE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallationStatus {}
+
+    /** @hide */
+    @IntDef(prefix = { "CAUSE_" }, value = {
+            CAUSE_NOT_SPECIFIED,
+            CAUSE_INSTALL_COMPLETED,
+            CAUSE_INSTALL_CANCELLED,
+            CAUSE_ERROR_IO,
+            CAUSE_ERROR_INVALID_URL,
+            CAUSE_ERROR_IPC,
+            CAUSE_ERROR_EXCEPTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusChangedCause {}
+
+    private static final String TAG = "DynAndroidClient";
+
+    private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
+
+
+    /** Listener for installation status update. */
+    public interface OnStatusChangedListener {
+        /**
+         * This callback is called when installation status is changed, and when the
+         * client is {@link #bind} to DynamicAndroid installation service.
+         *
+         * @param status status code, also defined in {@code DynamicAndroidClient}.
+         * @param cause cause code, also defined in {@code DynamicAndroidClient}.
+         * @param progress number of bytes installed.
+         */
+        void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
+                long progress);
+    }
+
+    /*
+     * Status codes
+     */
+    /** We are bound to installation service, but failed to get its status */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /** Installation is not started yet. */
+    public static final int STATUS_NOT_STARTED = 1;
+
+    /** Installation is in progress. */
+    public static final int STATUS_IN_PROGRESS = 2;
+
+    /** Installation is finished but the user has not launched it. */
+    public static final int STATUS_READY = 3;
+
+    /** Device is running in Dynamic Android. */
+    public static final int STATUS_IN_USE = 4;
+
+    /*
+     * Causes
+     */
+    /** Cause is not specified. This means the status is not changed. */
+    public static final int CAUSE_NOT_SPECIFIED = 0;
+
+    /** Status changed because installation is completed. */
+    public static final int CAUSE_INSTALL_COMPLETED = 1;
+
+    /** Status changed because installation is cancelled. */
+    public static final int CAUSE_INSTALL_CANCELLED = 2;
+
+    /** Installation failed due to IOException. */
+    public static final int CAUSE_ERROR_IO = 3;
+
+    /** Installation failed because the image URL source is not supported. */
+    public static final int CAUSE_ERROR_INVALID_URL = 4;
+
+    /** Installation failed due to IPC error. */
+    public static final int CAUSE_ERROR_IPC = 5;
+
+    /** Installation failed due to unhandled exception. */
+    public static final int CAUSE_ERROR_EXCEPTION = 6;
+
+    /*
+     * IPC Messages
+     */
+    /**
+     * Message to register listener.
+     * @hide
+     */
+    public static final int MSG_REGISTER_LISTENER = 1;
+
+    /**
+     * Message to unregister listener.
+     * @hide
+     */
+    public static final int MSG_UNREGISTER_LISTENER = 2;
+
+    /**
+     * Message for status update.
+     * @hide
+     */
+    public static final int MSG_POST_STATUS = 3;
+
+    /*
+     * Messages keys
+     */
+    /**
+     * Message key, for progress update.
+     * @hide
+     */
+    public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
+
+    /*
+     * Intent Actions
+     */
+    /**
+     * Intent action: start installation.
+     * @hide
+     */
+    public static final String ACTION_START_INSTALL =
+            "android.content.action.START_INSTALL";
+
+    /**
+     * Intent action: notify user if we are currently running in Dynamic Android.
+     * @hide
+     */
+    public static final String ACTION_NOTIFY_IF_IN_USE =
+            "android.content.action.NOTIFY_IF_IN_USE";
+
+    /*
+     * Intent Keys
+     */
+    /**
+     * Intent key: URL to system image.
+     * @hide
+     */
+    public static final String KEY_SYSTEM_URL = "KEY_SYSTEM_URL";
+
+    /**
+     * Intent key: Size of system image, in bytes.
+     * @hide
+     */
+    public static final String KEY_SYSTEM_SIZE = "KEY_SYSTEM_SIZE";
+
+    /**
+     * Intent key: Number of bytes to reserve for userdata.
+     * @hide
+     */
+    public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
+
+
+    private static class IncomingHandler extends Handler {
+        private final WeakReference<DynamicAndroidClient> mWeakClient;
+
+        IncomingHandler(DynamicAndroidClient service) {
+            super(Looper.getMainLooper());
+            mWeakClient = new WeakReference<>(service);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DynamicAndroidClient service = mWeakClient.get();
+
+            if (service != null) {
+                service.handleMessage(msg);
+            }
+        }
+    }
+
+    private class DynAndroidServiceConnection implements ServiceConnection {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Slog.v(TAG, "DynAndroidService connected");
+
+            mService = new Messenger(service);
+
+            try {
+                Message msg = Message.obtain(null, MSG_REGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to get status from installation service");
+                mExecutor.execute(() -> {
+                    mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0);
+                });
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Slog.v(TAG, "DynAndroidService disconnected");
+            mService = null;
+        }
+    }
+
+    private final Context mContext;
+    private final DynAndroidServiceConnection mConnection;
+    private final Messenger mMessenger;
+
+    private boolean mBound;
+    private Executor mExecutor;
+    private OnStatusChangedListener mListener;
+    private Messenger mService;
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public DynamicAndroidClient(@NonNull Context context) {
+        mContext = context;
+        mConnection = new DynAndroidServiceConnection();
+        mMessenger = new Messenger(new IncomingHandler(this));
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called using
+     * the executor.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull OnStatusChangedListener listener,
+            @NonNull @CallbackExecutor Executor executor) {
+        mListener = listener;
+        mExecutor = executor;
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called in main
+     * thread.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull OnStatusChangedListener listener) {
+        mListener = listener;
+        mExecutor = null;
+    }
+
+    /**
+     * Bind to DynamicAndroidInstallationService.
+     */
+    public void bind() {
+        Intent intent = new Intent();
+        intent.setClassName("com.android.dynandroid",
+                "com.android.dynandroid.DynamicAndroidInstallationService");
+
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+        mBound = true;
+    }
+
+    /**
+     * Unbind from DynamicAndroidInstallationService.
+     */
+    public void unbind() {
+        if (!mBound) {
+            return;
+        }
+
+        if (mService != null) {
+            try {
+                Message msg = Message.obtain(null, MSG_UNREGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to unregister from installation service");
+            }
+        }
+
+        // Detach our existing connection.
+        mContext.unbindService(mConnection);
+
+        mBound = false;
+    }
+
+    /**
+     * Start installing DynamicAndroid from URL with default userdata size.
+     *
+     * @param systemUrl A network URL or a file URL to system image.
+     * @param systemSize size of system image.
+     */
+    public void start(String systemUrl, long systemSize) {
+        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+    }
+
+    /**
+     * Start installing DynamicAndroid from URL.
+     *
+     * @param systemUrl A network URL or a file URL to system image.
+     * @param systemSize size of system image.
+     * @param userdataSize bytes reserved for userdata.
+     */
+    public void start(String systemUrl, long systemSize, long userdataSize) {
+        Intent intent = new Intent();
+
+        intent.setClassName("com.android.dynandroid",
+                "com.android.dynandroid.VerificationActivity");
+
+        intent.setAction(ACTION_START_INSTALL);
+
+        intent.putExtra(KEY_SYSTEM_URL, systemUrl);
+        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+        mContext.startActivity(intent);
+    }
+
+    private void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_POST_STATUS:
+                int status = msg.arg1;
+                int cause = msg.arg2;
+                // obj is non-null
+                long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE);
+
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        mListener.onStatusChanged(status, cause, progress);
+                    });
+                } else {
+                    mListener.onStatusChanged(status, cause, progress);
+                }
+                break;
+            default:
+                // do nothing
+
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ActivityPresentationInfo.java b/core/java/android/content/pm/ActivityPresentationInfo.java
new file mode 100644
index 0000000..ccc61dc
--- /dev/null
+++ b/core/java/android/content/pm/ActivityPresentationInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.content.pm;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+
+/**
+ * Holds basic information about an activity.
+ *
+ * @hide
+ */
+public final class ActivityPresentationInfo {
+    public final int taskId;
+    public final int displayId;
+
+    @NonNull
+    public final ComponentName componentName;
+
+    public ActivityPresentationInfo(int taskId, int displayId,
+            @NonNull ComponentName componentName) {
+        this.taskId = taskId;
+        this.displayId = displayId;
+        this.componentName = componentName;
+    }
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5d6d144..b27c5dc 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -640,19 +640,15 @@
     public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
 
     /**
-     * Indicate whether this application prefers code integrity, that is, run only code that is
-     * signed. This requires android:extractNativeLibs to be "false", as well as .dex and .so (if
-     * any) stored uncompressed inside the APK, which is signed. At run time, the implications
-     * include:
-     *
-     * <ul>
-     * <li>ART will JIT the dex code directly from the APK. There may be performance characteristic
-     * changes depend on the actual workload.
-     * </ul>
+     * Indicates whether this application wants to use the embedded dex in the APK, rather than
+     * extracted or locally compiled variants. This keeps the dex code protected by the APK
+     * signature. Such apps will always run in JIT mode (same when they are first installed), and
+     * the system will never generate ahead-of-time compiled code for them. Depending on the app's
+     * workload, there may be some run time performance change, noteably the cold start time.
      *
      * @hide
      */
-    public static final int PRIVATE_FLAG_PREFER_CODE_INTEGRITY = 1 << 25;
+    public static final int PRIVATE_FLAG_USE_EMBEDDED_DEX = 1 << 25;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -669,7 +665,7 @@
             PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
             PRIVATE_FLAG_OEM,
             PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
-            PRIVATE_FLAG_PREFER_CODE_INTEGRITY,
+            PRIVATE_FLAG_USE_EMBEDDED_DEX,
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_PRODUCT_SERVICES,
@@ -1192,6 +1188,9 @@
     /** @hide */
     public boolean hiddenUntilInstalled;
 
+    /** @hide */
+    public String zygotePreloadName;
+
     /**
      * Represents the default policy. The actual policy used will depend on other properties of
      * the application, e.g. the target SDK version.
@@ -1533,6 +1532,7 @@
         compileSdkVersionCodename = orig.compileSdkVersionCodename;
         mHiddenApiPolicy = orig.mHiddenApiPolicy;
         hiddenUntilInstalled = orig.hiddenUntilInstalled;
+        zygotePreloadName = orig.zygotePreloadName;
     }
 
     public String toString() {
@@ -1609,6 +1609,7 @@
         dest.writeString(appComponentFactory);
         dest.writeInt(mHiddenApiPolicy);
         dest.writeInt(hiddenUntilInstalled ? 1 : 0);
+        dest.writeString(zygotePreloadName);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1682,6 +1683,7 @@
         appComponentFactory = source.readString();
         mHiddenApiPolicy = source.readInt();
         hiddenUntilInstalled = source.readInt() != 0;
+        zygotePreloadName = source.readString();
     }
 
     /**
@@ -1956,8 +1958,8 @@
     }
 
     /** @hide */
-    public boolean isCodeIntegrityPreferred() {
-        return (privateFlags & PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0;
+    public boolean isEmbeddedDexUsed() {
+        return (privateFlags & PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0;
     }
 
     /**
diff --git a/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml b/core/java/android/content/pm/BasicActivityInfo.aidl
similarity index 60%
copy from packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
copy to core/java/android/content/pm/BasicActivityInfo.aidl
index 4bac7da..f04bf9a 100644
--- a/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
+++ b/core/java/android/content/pm/BasicActivityInfo.aidl
@@ -1,12 +1,11 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open 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,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Headline / Body font Rubik overlay -->
-    <string name="font_rubik_rubik_overlay" translatable="false">Rubik / Rubik</string>
-</resources>
+
+package android.content.pm;
+
+/** @hide */
+parcelable BasicActivityInfo;
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index d1bc377..50bb3c7 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -24,6 +24,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.LauncherApps;
+import android.content.pm.IPackageInstallerCallback;
+import android.content.pm.PackageInstaller;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
@@ -44,6 +46,9 @@
             String callingPackage, String packageName, in UserHandle user);
     ActivityInfo resolveActivity(
             String callingPackage, in ComponentName component, in UserHandle user);
+    void startSessionDetailsActivityAsUser(in IApplicationThread caller, String callingPackage,
+                in PackageInstaller.SessionInfo sessionInfo, in Rect sourceBounds, in Bundle opts,
+                in UserHandle user);
     void startActivityAsUser(in IApplicationThread caller, String callingPackage,
             in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
@@ -79,4 +84,9 @@
             String callingPackage, String packageName, in UserHandle user);
     IntentSender getShortcutConfigActivityIntent(String callingPackage, in ComponentName component,
             in UserHandle user);
+
+    // Unregister is performed using package installer
+    void registerPackageInstallerCallback(String callingPackage,
+            in IPackageInstallerCallback callback);
+    ParceledListSlice getAllSessions(String callingPackage);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a6a6f01..36ffb0e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -686,6 +686,8 @@
 
     String getWellbeingPackageName();
 
+    String getAppPredictionServicePackageName();
+
     String getContentCaptureServicePackageName();
 
     String getIncidentReportApproverPackageName();
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 98dd9b3..b0d16cd 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +33,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageInstaller.SessionCallback;
+import android.content.pm.PackageInstaller.SessionCallbackDelegate;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -65,7 +69,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Class for retrieving a list of launchable activities for the current user and any associated
@@ -154,8 +160,8 @@
     private final PackageManager mPm;
     private final UserManager mUserManager;
 
-    private List<CallbackMessageHandler> mCallbacks
-            = new ArrayList<CallbackMessageHandler>();
+    private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
+    private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();
 
     /**
      * Callbacks for package changes to this and related managed profiles.
@@ -572,6 +578,24 @@
     }
 
     /**
+     * Starts an activity to show the details of the specified session.
+     *
+     * @param sessionInfo The SessionInfo of the session
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon
+     * @param opts Options to pass to startActivity
+     */
+    public void startPackageInstallerSessionDetailsActivity(SessionInfo sessionInfo,
+            Rect sourceBounds, Bundle opts) {
+        try {
+            mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), sessionInfo, sourceBounds, opts,
+                    sessionInfo.getUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starts the settings activity to show the application details for a
      * package in the specified profile.
      *
@@ -1131,7 +1155,7 @@
     }
 
     /**
-     * Registers a callback for changes to packages in current and managed profiles.
+     * Registers a callback for changes to packages in this user and managed profiles.
      *
      * @param callback The callback to register.
      */
@@ -1140,7 +1164,7 @@
     }
 
     /**
-     * Registers a callback for changes to packages in current and managed profiles.
+     * Registers a callback for changes to packages in this user and managed profiles.
      *
      * @param callback The callback to register.
      * @param handler that should be used to post callbacks on, may be null.
@@ -1446,6 +1470,64 @@
     }
 
     /**
+     * Register a callback to watch for session lifecycle events in this user and managed profiles.
+     * @param callback The callback to register.
+     * @param executor {@link Executor} to handle the callbacks, cannot be null.
+     *
+     * @see PackageInstaller#registerSessionCallback(SessionCallback)
+     */
+    public void registerPackageInstallerSessionCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull SessionCallback callback) {
+        if (executor == null) {
+            throw new NullPointerException("Executor must not be null");
+        }
+
+        synchronized (mDelegates) {
+            final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
+                    executor);
+            try {
+                mService.registerPackageInstallerCallback(mContext.getPackageName(),
+                        delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /**
+     * Unregisters a callback that was previously registered.
+     *
+     * @param callback The callback to unregister.
+     * @see #registerPackageInstallerSessionCallback(Executor, SessionCallback)
+     */
+    public void unregisterPackageInstallerSessionCallback(SessionCallback callback) {
+        synchronized (mDelegates) {
+            for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final SessionCallbackDelegate delegate = i.next();
+                if (delegate.mCallback == callback) {
+                    mPm.getPackageInstaller().unregisterSessionCallback(delegate.mCallback);
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Return list of all known install sessions in this user and managed profiles, regardless
+     * of the installer.
+     *
+     * @see PackageInstaller#getAllSessions()
+     */
+    public @NonNull List<SessionInfo> getAllPackageInstallerSessions() {
+        try {
+            return mService.getAllSessions(mContext.getPackageName()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * A helper method to extract a {@link PinItemRequest} set to
      * the {@link #EXTRA_PIN_ITEM_REQUEST} extra.
      */
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
index 07e640b..044e87d 100644
--- a/core/java/android/content/pm/ModuleInfo.java
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -32,7 +32,7 @@
      // constructor, and writeToParcel.
 
     /** Public name of this module. */
-    private String mName;
+    private CharSequence mName;
 
     /** The package name of this module. */
     private String mPackageName;
@@ -57,13 +57,13 @@
     }
 
     /** @hide Sets the public name of this module. */
-    public ModuleInfo setName(String name) {
+    public ModuleInfo setName(CharSequence name) {
         mName = name;
         return this;
     }
 
     /** Gets the public name of this module. */
-    public @Nullable String getName() {
+    public @Nullable CharSequence getName() {
         return mName;
     }
 
@@ -123,13 +123,13 @@
 
     /** Flattens this object into the given {@link Parcel}. */
     public void writeToParcel(Parcel dest, int parcelableFlags) {
-        dest.writeString(mName);
+        dest.writeCharSequence(mName);
         dest.writeString(mPackageName);
         dest.writeBoolean(mHidden);
     }
 
     private ModuleInfo(Parcel source) {
-        mName = source.readString();
+        mName = source.readCharSequence();
         mPackageName = source.readString();
         mHidden = source.readBoolean();
     }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2dc014c..8b8f3e5 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -36,20 +36,21 @@
 import android.os.Build;
 import android.os.FileBridge;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.os.HandlerExecutor;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.ExceptionUtils;
 
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -61,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Offers the ability to install, upgrade, and remove applications on the
@@ -659,8 +661,7 @@
     }
 
     /** {@hide} */
-    private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
-            Handler.Callback {
+    static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub {
         private static final int MSG_SESSION_CREATED = 1;
         private static final int MSG_SESSION_BADGING_CHANGED = 2;
         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
@@ -668,63 +669,41 @@
         private static final int MSG_SESSION_FINISHED = 5;
 
         final SessionCallback mCallback;
-        final Handler mHandler;
+        final Executor mExecutor;
 
-        public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
+        SessionCallbackDelegate(SessionCallback callback, Executor executor) {
             mCallback = callback;
-            mHandler = new Handler(looper, this);
-        }
-
-        @Override
-        public boolean handleMessage(Message msg) {
-            final int sessionId = msg.arg1;
-            switch (msg.what) {
-                case MSG_SESSION_CREATED:
-                    mCallback.onCreated(sessionId);
-                    return true;
-                case MSG_SESSION_BADGING_CHANGED:
-                    mCallback.onBadgingChanged(sessionId);
-                    return true;
-                case MSG_SESSION_ACTIVE_CHANGED:
-                    final boolean active = msg.arg2 != 0;
-                    mCallback.onActiveChanged(sessionId, active);
-                    return true;
-                case MSG_SESSION_PROGRESS_CHANGED:
-                    mCallback.onProgressChanged(sessionId, (float) msg.obj);
-                    return true;
-                case MSG_SESSION_FINISHED:
-                    mCallback.onFinished(sessionId, msg.arg2 != 0);
-                    return true;
-            }
-            return false;
+            mExecutor = executor;
         }
 
         @Override
         public void onSessionCreated(int sessionId) {
-            mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onCreated, mCallback,
+                    sessionId).recycleOnUse());
         }
 
         @Override
         public void onSessionBadgingChanged(int sessionId) {
-            mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onBadgingChanged,
+                    mCallback, sessionId).recycleOnUse());
         }
 
         @Override
         public void onSessionActiveChanged(int sessionId, boolean active) {
-            mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0)
-                    .sendToTarget();
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onActiveChanged,
+                    mCallback, sessionId, active).recycleOnUse());
         }
 
         @Override
         public void onSessionProgressChanged(int sessionId, float progress) {
-            mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
-                    .sendToTarget();
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onProgressChanged,
+                    mCallback, sessionId, progress).recycleOnUse());
         }
 
         @Override
         public void onSessionFinished(int sessionId, boolean success) {
-            mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
-                    .sendToTarget();
+            mExecutor.execute(PooledLambda.obtainRunnable(SessionCallback::onFinished,
+                    mCallback, sessionId, success).recycleOnUse());
         }
     }
 
@@ -758,7 +737,7 @@
     public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
         synchronized (mDelegates) {
             final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
-                    handler.getLooper());
+                    new HandlerExecutor(handler));
             try {
                 mInstaller.registerCallback(delegate, mUserId);
             } catch (RemoteException e) {
@@ -1311,6 +1290,28 @@
             isStaged = source.readBoolean();
         }
 
+        /** {@hide} */
+        public SessionParams copy() {
+            SessionParams ret = new SessionParams(mode);
+            ret.installFlags = installFlags;
+            ret.installLocation = installLocation;
+            ret.installReason = installReason;
+            ret.sizeBytes = sizeBytes;
+            ret.appPackageName = appPackageName;
+            ret.appIcon = appIcon;  // not a copy.
+            ret.appLabel = appLabel;
+            ret.originatingUri = originatingUri;  // not a copy, but immutable.
+            ret.originatingUid = originatingUid;
+            ret.referrerUri = referrerUri;  // not a copy, but immutable.
+            ret.abiOverride = abiOverride;
+            ret.volumeUuid = volumeUuid;
+            ret.grantedRuntimePermissions = grantedRuntimePermissions;
+            ret.installerPackageName = installerPackageName;
+            ret.isMultiPackage = isMultiPackage;
+            ret.isStaged = isStaged;
+            return ret;
+        }
+
         /**
          * Check if there are hidden options set.
          *
@@ -1649,6 +1650,8 @@
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
         /** {@hide} */
+        public int userId;
+        /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public String installerPackageName;
         /** {@hide} */
@@ -1720,6 +1723,7 @@
         /** {@hide} */
         public SessionInfo(Parcel source) {
             sessionId = source.readInt();
+            userId = source.readInt();
             installerPackageName = source.readString();
             resolvedBaseCodePath = source.readString();
             progress = source.readFloat();
@@ -1761,6 +1765,13 @@
         }
 
         /**
+         * Return the user associated with this session.
+         */
+        public UserHandle getUser() {
+            return new UserHandle(userId);
+        }
+
+        /**
          * Return the package name of the app that owns this session.
          */
         public @Nullable String getInstallerPackageName() {
@@ -2091,6 +2102,7 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(sessionId);
+            dest.writeInt(userId);
             dest.writeString(installerPackageName);
             dest.writeString(resolvedBaseCodePath);
             dest.writeFloat(progress);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d88f36c..e2907e2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -552,8 +552,6 @@
      * currently active, i.e. mounted and available to other processes of the OS.
      * In particular, this flag alone will not match APEX files that are staged
      * for activation at next reboot.
-     * TODO(b/119767311): include uninstalled/inactive APEX if
-     *                    MATCH_UNINSTALLED_PACKAGES is set.
      */
     public static final int MATCH_APEX = 0x40000000;
 
@@ -6727,6 +6725,16 @@
     }
 
     /**
+     * @return the system defined app predictor package name, or null if there's none.
+     *
+     * @hide
+     */
+    public String getAppPredictionServicePackageName() {
+        throw new UnsupportedOperationException(
+            "getAppPredictionServicePackageName not implemented in subclass");
+    }
+
+    /**
      * @return the system defined content capture service package name, or null if there's none.
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 738730e..4a2dbe7 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -57,6 +57,7 @@
     public static final int PACKAGE_DOCUMENTER = 8;
     public static final int PACKAGE_CONFIGURATOR = 9;
     public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
+    public static final int PACKAGE_APP_PREDICTOR = 11;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -69,6 +70,7 @@
         PACKAGE_DOCUMENTER,
         PACKAGE_CONFIGURATOR,
         PACKAGE_INCIDENT_REPORT_APPROVER,
+        PACKAGE_APP_PREDICTOR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eb59cfc..2c2d6f2 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -475,7 +475,7 @@
         public final boolean extractNativeLibs;
         public final boolean isolatedSplits;
         public final boolean isSplitRequired;
-        public final boolean preferCodeIntegrity;
+        public final boolean useEmbeddedDex;
 
         public ApkLite(String codePath, String packageName, String splitName,
                 boolean isFeatureSplit,
@@ -484,7 +484,7 @@
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                 SigningDetails signingDetails, boolean coreApp,
                 boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean preferCodeIntegrity, boolean extractNativeLibs, boolean isolatedSplits) {
+                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -501,7 +501,7 @@
             this.debuggable = debuggable;
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
-            this.preferCodeIntegrity = preferCodeIntegrity;
+            this.useEmbeddedDex = useEmbeddedDex;
             this.extractNativeLibs = extractNativeLibs;
             this.isolatedSplits = isolatedSplits;
             this.isSplitRequired = isSplitRequired;
@@ -1726,7 +1726,7 @@
         boolean isolatedSplits = false;
         boolean isFeatureSplit = false;
         boolean isSplitRequired = false;
-        boolean preferCodeIntegrity = false;
+        boolean useEmbeddedDex = false;
         String configForSplit = null;
         String usesSplitName = null;
 
@@ -1790,8 +1790,8 @@
                         extractNativeLibsProvided = Boolean.valueOf(
                                 attrs.getAttributeBooleanValue(i, true));
                     }
-                    if ("preferCodeIntegrity".equals(attr)) {
-                        preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
+                    if ("useEmbeddedDex".equals(attr)) {
+                        useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
                     }
                 }
             } else if (TAG_USES_SPLIT.equals(parser.getName())) {
@@ -1851,16 +1851,10 @@
         final boolean extractNativeLibs = (extractNativeLibsProvided != null)
                 ? extractNativeLibsProvided : extractNativeLibsDefault;
 
-        if (preferCodeIntegrity && extractNativeLibs) {
-            throw new PackageParserException(
-                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                    "Can't request both preferCodeIntegrity and extractNativeLibs");
-        }
-
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                 configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                 revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
-                multiArch, use32bitAbi, preferCodeIntegrity, extractNativeLibs, isolatedSplits);
+                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits);
     }
 
     /**
@@ -3789,9 +3783,9 @@
         }
 
         if (sa.getBoolean(
-                R.styleable.AndroidManifestApplication_preferCodeIntegrity,
+                R.styleable.AndroidManifestApplication_useEmbeddedDex,
                 false)) {
-            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY;
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX;
         }
 
         if (sa.getBoolean(
@@ -3907,6 +3901,9 @@
             outError[0] = "Invalid class loader name: " + ai.classLoaderName;
         }
 
+        ai.zygotePreloadName = sa.getString(
+                com.android.internal.R.styleable.AndroidManifestApplication_zygotePreloadName);
+
         sa.recycle();
 
         if (outError[0] != null) {
@@ -4426,7 +4423,7 @@
         a.owner = owner;
         a.setPackageName(owner.packageName);
 
-        a.info.theme = 0;
+        a.info.theme = android.R.style.Theme_NoDisplay;
         a.info.exported = true;
         a.info.name = AppDetailsActivity.class.getName();
         a.info.processName = owner.applicationInfo.processName;
@@ -5573,7 +5570,7 @@
 
         s.info.mForegroundServiceType = sa.getInt(
                 com.android.internal.R.styleable.AndroidManifestService_foregroundServiceType,
-                ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED);
+                ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
 
         s.info.flags = 0;
         if (sa.getBoolean(
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index e776984..fb63e0d 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -224,6 +224,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 0x100000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>app_predictor</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -244,6 +255,7 @@
             PROTECTION_FLAG_DOCUMENTER,
             PROTECTION_FLAG_CONFIGURATOR,
             PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
+            PROTECTION_FLAG_APP_PREDICTOR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -445,6 +457,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0) {
             protLevel += "|incidentReportApprover";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
+            protLevel += "|appPredictor";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 8300c0c..60475de 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -101,77 +101,73 @@
     /**
      * The default foreground service type if not been set in manifest file.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0;
+    public static final int FOREGROUND_SERVICE_TYPE_NONE = 0;
 
     /**
-     * Constant corresponding to <code>sync</code> in
+     * Constant corresponding to <code>dataSync</code> in
      * the {@link android.R.attr#foregroundServiceType} attribute.
      * Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
      * transfer over network between device and cloud.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1;
+    public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1 << 0;
 
     /**
-     * Constant corresponding to <code>mediaPlay</code> in
+     * Constant corresponding to <code>mediaPlayback</code> in
      * the {@link android.R.attr#foregroundServiceType} attribute.
-     * Music, video, news or other media play.
+     * Music, video, news or other media playback.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2;
+    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 1 << 1;
 
     /**
      * Constant corresponding to <code>phoneCall</code> in
      * the {@link android.R.attr#foregroundServiceType} attribute.
      * Ongoing phone call or video conference.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3;
+    public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 1 << 2;
 
     /**
      * Constant corresponding to <code>location</code> in
      * the {@link android.R.attr#foregroundServiceType} attribute.
      * GPS, map, navigation location update.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4;
+    public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 1 << 3;
 
     /**
-     * Constant corresponding to <code>deviceCompanion</code> in
+     * Constant corresponding to <code>connectedDevice</code> in
      * the {@link android.R.attr#foregroundServiceType} attribute.
      * Auto, bluetooth, TV or other devices connection, monitoring and interaction.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5;
+    public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 1 << 4;
 
     /**
-     * Constant corresponding to <code>ongoingProcess</code> in
-     * the {@link android.R.attr#foregroundServiceType} attribute.
-     * Process that should not be interrupted, including installation, setup, photo
-     * compression etc.
+     * A special value indicates to use all types set in manifest file.
      */
-    public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6;
+    public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1;
 
     /**
-     * The enumeration of values for foreground service type.
+     * The set of flags for foreground service type.
      * The foreground service type is set in {@link android.R.attr#foregroundServiceType}
      * attribute.
      * @hide
      */
-    @IntDef(flag = false, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
-            FOREGROUND_SERVICE_TYPE_UNSPECIFIED,
-            FOREGROUND_SERVICE_TYPE_SYNC,
-            FOREGROUND_SERVICE_TYPE_MEDIA_PLAY,
+    @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+            FOREGROUND_SERVICE_TYPE_NONE,
+            FOREGROUND_SERVICE_TYPE_DATA_SYNC,
+            FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
             FOREGROUND_SERVICE_TYPE_PHONE_CALL,
             FOREGROUND_SERVICE_TYPE_LOCATION,
-            FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION,
-            FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS
+            FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ForegroundServiceType {}
 
     /**
      * The type of foreground service, set in
-     * {@link android.R.attr#foregroundServiceType} attribute, one value in
+     * {@link android.R.attr#foregroundServiceType} attribute by ORing flags in
      * {@link ForegroundServiceType}
      * @hide
      */
-    public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_UNSPECIFIED;
+    public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
 
     public ServiceInfo() {
     }
@@ -217,6 +213,7 @@
         super.writeToParcel(dest, parcelableFlags);
         dest.writeString(permission);
         dest.writeInt(flags);
+        dest.writeInt(mForegroundServiceType);
     }
 
     public static final Creator<ServiceInfo> CREATOR =
@@ -233,5 +230,6 @@
         super(source);
         permission = source.readString();
         flags = source.readInt();
+        mForegroundServiceType = source.readInt();
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index baf64ad..59db49e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1394,6 +1394,17 @@
         mResourcesImpl.getValue(name, outValue, resolveRefs);
     }
 
+
+    /**
+     * @param set AttributeSet for which we want to find the source.
+     * @return The resource id for the source that is backing the given AttributeSet
+     * @hide
+     */
+    @AnyRes
+    public static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        return ResourcesImpl.getAttributeSetSourceResId(set);
+    }
+
     /**
      * This class holds the current attribute values for a particular theme.
      * In other words, a Theme is a set of values for resource attributes;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index d8564d5..9898079 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -15,6 +15,8 @@
  */
 package android.content.res;
 
+import static android.content.res.Resources.ID_NULL;
+
 import android.animation.Animator;
 import android.animation.StateListAnimator;
 import android.annotation.AnyRes;
@@ -1222,7 +1224,7 @@
                     for (int i = 0; i < num; i++) {
                         if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
                                 && cachedXmlBlockFiles[i].equals(file)) {
-                            return cachedXmlBlocks[i].newParser();
+                            return cachedXmlBlocks[i].newParser(id);
                         }
                     }
 
@@ -1239,7 +1241,7 @@
                         cachedXmlBlockCookies[pos] = assetCookie;
                         cachedXmlBlockFiles[pos] = file;
                         cachedXmlBlocks[pos] = block;
-                        return block.newParser();
+                        return block.newParser(id);
                     }
                 }
             } catch (Exception e) {
@@ -1299,6 +1301,14 @@
         }
     }
 
+    @AnyRes
+    static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        if (set == null) {
+            return ID_NULL;
+        }
+        return ((XmlBlock.Parser) set).getSourceResId();
+    }
+
     LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
         return sPreloadedDrawables[0];
     }
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index 4e1159a..d8c3dde 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,6 +16,9 @@
 
 package android.content.res;
 
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.AnyRes;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.util.TypedValue;
@@ -73,19 +76,29 @@
 
     @UnsupportedAppUsage
     public XmlResourceParser newParser() {
+        return newParser(ID_NULL);
+    }
+
+    public XmlResourceParser newParser(@AnyRes int resId) {
         synchronized (this) {
             if (mNative != 0) {
-                return new Parser(nativeCreateParseState(mNative), this);
+                return new Parser(nativeCreateParseState(mNative), this, resId);
             }
             return null;
         }
     }
 
     /*package*/ final class Parser implements XmlResourceParser {
-        Parser(long parseState, XmlBlock block) {
+        Parser(long parseState, XmlBlock block, @AnyRes int sourceResId) {
             mParseState = parseState;
             mBlock = block;
             block.mOpenCount++;
+            mSourceResId = sourceResId;
+        }
+
+        @AnyRes
+        public int getSourceResId() {
+            return mSourceResId;
         }
 
         public void setFeature(String name, boolean state) throws XmlPullParserException {
@@ -473,6 +486,7 @@
         private boolean mDecNextDepth = false;
         private int mDepth = 0;
         private int mEventType = START_DOCUMENT;
+        private @AnyRes int mSourceResId;
     }
 
     protected void finalize() throws Throwable {
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 32e2198..104661f 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -32,7 +32,7 @@
     // Exposed for use from the system server only. Callback from the package
     // manager during the install flow when user data can be restored for a given
     // package.
-    void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+    void restoreUserData(String packageName, in int[] userIds, int appId, long ceDataInode,
             String seInfo, int token);
 
     // Exposed for test purposes only.
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 5d4928c..475be49 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -20,6 +20,7 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.graphics.GraphicBuffer;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -214,6 +215,19 @@
     }
 
     /**
+     * @hide
+     * Returns a <code>HardwareBuffer</code> instance from <code>GraphicBuffer</code>
+     *
+     * @param graphicBuffer A GraphicBuffer to be wrapped as HardwareBuffer
+     * @return A <code>HardwareBuffer</code> instance.
+     */
+    @NonNull
+    public static HardwareBuffer createFromGraphicBuffer(@NonNull GraphicBuffer graphicBuffer) {
+        long nativeObject = nCreateFromGraphicBuffer(graphicBuffer);
+        return new HardwareBuffer(nativeObject);
+    }
+
+    /**
      * Private use only. See {@link #create(int, int, int, int, long)}. May also be
      * called from JNI using an already allocated native <code>HardwareBuffer</code>.
      */
@@ -405,6 +419,7 @@
 
     private static native long nCreateHardwareBuffer(int width, int height, int format, int layers,
             long usage);
+    private static native long nCreateFromGraphicBuffer(GraphicBuffer graphicBuffer);
     private static native long nGetNativeFinalizer();
     private static native void nWriteHardwareBufferToParcel(long nativeObject, Parcel dest);
     private static native long nReadHardwareBufferFromParcel(Parcel in);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 5afe1a9..d257c03 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
@@ -29,6 +30,7 @@
 /**
  * A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
  */
+@SystemService(Context.BIOMETRIC_SERVICE)
 public class BiometricManager {
 
     private static final String TAG = "BiometricManager";
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index ec62aba..d569a78 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -202,6 +202,9 @@
          * Required: Set the text for the negative button. This would typically be used as a
          * "Cancel" button, but may be also used to show an alternative method for authentication,
          * such as screen that asks for a backup password.
+         *
+         * Note that this should not be set if {@link #setEnableFallback(boolean)} is set to true.
+         *
          * @param text
          * @return
          */
@@ -248,6 +251,10 @@
         /**
          * The user will first be prompted to authenticate with biometrics, but also given the
          * option to authenticate with their device PIN, pattern, or password.
+         *
+         * Note that {@link #setNegativeButton(CharSequence, Executor,
+         * DialogInterface.OnClickListener)} should not be set if this is set to true.
+         *
          * @param enable When true, the prompt will fall back to ask for the user's device
          *               credentials (PIN, pattern, or password).
          * @return
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 02eb28c..c6186bb 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -16,11 +16,15 @@
 
 package android.hardware.display;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Data about a brightness settings change.
  *
@@ -72,12 +76,29 @@
     /** Whether brightness curve includes a user brightness point */
     public final boolean isUserSetBrightness;
 
+    /**
+     * Histogram counting how many times a pixel of a given value was displayed onscreen for the
+     * Value component of HSV if the device supports color sampling, if the device does not support
+     * color sampling the value will be null.
+     * The buckets of the histogram are evenly weighted, the number of buckets is device specific.
+     * For example if we had {10, 6, 4, 1} this means that 10 pixels were in the range
+     * [0x00,0x3f], 6 pixels were in the range [0x40,0x7f] etc.
+     */
+    @Nullable
+    public final long[] colorValueBuckets;
+
+    /**
+     * How many milliseconds of data are contained in the colorValueBuckets.
+     */
+    public final long colorSampleDuration;
+
 
     /** @hide */
     private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
             int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
             float powerBrightnessFactor, boolean nightMode, int colorTemperature,
-            float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
+            float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
+            long[] colorValueBuckets, long colorSampleDuration) {
         this.brightness = brightness;
         this.timeStamp = timeStamp;
         this.packageName = packageName;
@@ -91,6 +112,8 @@
         this.lastBrightness = lastBrightness;
         this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
         this.isUserSetBrightness = isUserSetBrightness;
+        this.colorValueBuckets = colorValueBuckets;
+        this.colorSampleDuration = colorSampleDuration;
     }
 
     /** @hide */
@@ -108,6 +131,8 @@
         this.lastBrightness = other.lastBrightness;
         this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
         this.isUserSetBrightness = other.isUserSetBrightness;
+        this.colorValueBuckets = other.colorValueBuckets;
+        this.colorSampleDuration = other.colorSampleDuration;
     }
 
     private BrightnessChangeEvent(Parcel source) {
@@ -124,6 +149,8 @@
         lastBrightness = source.readFloat();
         isDefaultBrightnessConfig = source.readBoolean();
         isUserSetBrightness = source.readBoolean();
+        colorValueBuckets = source.createLongArray();
+        colorSampleDuration = source.readLong();
     }
 
     public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -156,6 +183,8 @@
         dest.writeFloat(lastBrightness);
         dest.writeBoolean(isDefaultBrightnessConfig);
         dest.writeBoolean(isUserSetBrightness);
+        dest.writeLongArray(colorValueBuckets);
+        dest.writeLong(colorSampleDuration);
     }
 
     /** @hide */
@@ -173,6 +202,8 @@
         private float mLastBrightness;
         private boolean mIsDefaultBrightnessConfig;
         private boolean mIsUserSetBrightness;
+        private long[] mColorValueBuckets;
+        private long mColorSampleDuration;
 
         /** {@see BrightnessChangeEvent#brightness} */
         public Builder setBrightness(float brightness) {
@@ -252,12 +283,21 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#valueBuckets} */
+        public Builder setColorValues(@NonNull long[] colorValueBuckets, long colorSampleDuration) {
+            Objects.requireNonNull(colorValueBuckets);
+            mColorValueBuckets = colorValueBuckets;
+            mColorSampleDuration = colorSampleDuration;
+            return this;
+        }
+
         /** Builds a BrightnessChangeEvent */
         public BrightnessChangeEvent build() {
             return new BrightnessChangeEvent(mBrightness, mTimeStamp,
                     mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
                     mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
-                    mIsDefaultBrightnessConfig, mIsUserSetBrightness);
+                    mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
+                    mColorSampleDuration);
         }
     }
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ef8ca9e..1656c6f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1033,34 +1033,6 @@
         }
     }
 
-    /**
-     * Configures an always-on VPN connection through a specific application.
-     * This connection is automatically granted and persisted after a reboot.
-     *
-     * <p>The designated package should declare a {@link VpnService} in its
-     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
-     *    otherwise the call will fail.
-     *
-     * @param userId The identifier of the user to set an always-on VPN for.
-     * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
-     *                   to remove an existing always-on VPN configuration.
-     * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
-     *        {@code false} otherwise.
-     * @return {@code true} if the package is set as always-on VPN controller;
-     *         {@code false} otherwise.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
-    public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
-            boolean lockdownEnabled) {
-        try {
-            return mService.setAlwaysOnVpnPackage(
-                    userId, vpnPackage, lockdownEnabled, /* whitelist */ null);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
    /**
      * Returns the package name of the currently set always-on VPN application.
      * If there is no always-on VPN set, or the VPN is provided by the system instead
@@ -2609,6 +2581,7 @@
     }
 
     /** {@hide} */
+    @SystemApi
     public static final int TETHER_ERROR_NO_ERROR           = 0;
     /** {@hide} */
     public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
@@ -2631,9 +2604,13 @@
     /** {@hide} */
     public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
     /** {@hide} */
+    @SystemApi
     public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
     /** {@hide} */
     public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
+    /** {@hide} */
+    @SystemApi
+    public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN  = 13;
 
     /**
      * Get a more detailed error code after a Tethering or Untethering
@@ -2656,6 +2633,65 @@
     }
 
     /**
+     * Callback for use with {@link #getLatestTetheringEntitlementValue} to find out whether
+     * entitlement succeeded.
+     * @hide
+     */
+    @SystemApi
+    public abstract static class TetheringEntitlementValueListener  {
+        /**
+         * Called to notify entitlement result.
+         *
+         * @param resultCode a int value of entitlement result. It may be one of
+         *         {@link #TETHER_ERROR_NO_ERROR},
+         *         {@link #TETHER_ERROR_PROVISION_FAILED}, or
+         *         {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}.
+         */
+        public void onEntitlementResult(int resultCode) {}
+    }
+
+    /**
+     * Get the last value of the entitlement check on this downstream. If the cached value is
+     * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
+     * cached value. Otherwise, a UI-based entitlement check would be performed. It is not
+     * guaranteed that the UI-based entitlement check will complete in any specific time period
+     * and may in fact never complete. Any successful entitlement check the platform performs for
+     * any reason will update the cached value.
+     *
+     * @param type the downstream type of tethering. Must be one of
+     *         {@link #TETHERING_WIFI},
+     *         {@link #TETHERING_USB}, or
+     *         {@link #TETHERING_BLUETOOTH}.
+     * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+     * @param listener an {@link TetheringEntitlementValueListener} which will be called to notify
+     *         the caller of the result of entitlement check. The listener may be called zero or
+     *         one time.
+     * @param handler {@link Handler} to specify the thread upon which the listener will be invoked.
+     * {@hide}
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+    public void getLatestTetheringEntitlementValue(int type, boolean showEntitlementUi,
+            @NonNull final TetheringEntitlementValueListener listener, @Nullable Handler handler) {
+        Preconditions.checkNotNull(listener, "TetheringEntitlementValueListener cannot be null.");
+        ResultReceiver wrappedListener = new ResultReceiver(handler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                listener.onEntitlementResult(resultCode);
+            }
+        };
+
+        try {
+            String pkgName = mContext.getOpPackageName();
+            Log.i(TAG, "getLatestTetheringEntitlementValue:" + pkgName);
+            mService.getLatestTetheringEntitlementValue(type, wrappedListener,
+                    showEntitlementUi, pkgName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Report network connectivity status.  This is currently used only
      * to alter status bar UI.
      * <p>This method requires the caller to hold the permission
@@ -3986,10 +4022,17 @@
     @Deprecated
     public static boolean setProcessDefaultNetwork(@Nullable Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
-        if (netId == NetworkUtils.getBoundNetworkForProcess()) {
-            return true;
+        boolean isSameNetId = (netId == NetworkUtils.getBoundNetworkForProcess());
+
+        if (netId != NETID_UNSET) {
+            netId = network.getNetIdForResolv();
         }
-        if (NetworkUtils.bindProcessToNetwork(netId)) {
+
+        if (!NetworkUtils.bindProcessToNetwork(netId)) {
+            return false;
+        }
+
+        if (!isSameNetId) {
             // Set HTTP proxy system properties to match network.
             // TODO: Deprecate this static method and replace it with a non-static version.
             try {
@@ -4003,10 +4046,9 @@
             // Must flush socket pool as idle sockets will be bound to previous network and may
             // cause subsequent fetches to be performed on old network.
             NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
-            return true;
-        } else {
-            return false;
         }
+
+        return true;
     }
 
     /**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index f88adc2..1148ac1 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -197,4 +197,7 @@
     int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
     boolean isCallerCurrentAlwaysOnVpnApp();
     boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+
+    void getLatestTetheringEntitlementValue(int type, in ResultReceiver receiver,
+            boolean showEntitlementUi, String callerPkg);
 }
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index ac6bff0..b6cd635 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -20,7 +20,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -46,9 +48,22 @@
  * @hide
  */
 @SystemService(Context.NETWORK_STACK_SERVICE)
+@SystemApi
+@TestApi
 public class NetworkStack {
     private static final String TAG = NetworkStack.class.getSimpleName();
 
+    /**
+     * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
+     * protection level.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String PERMISSION_MAINLINE_NETWORK_STACK =
+            "android.permission.MAINLINE_NETWORK_STACK";
+
+    /** @hide */
     public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
 
     private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
@@ -66,12 +81,14 @@
         void onNetworkStackConnected(INetworkStackConnector connector);
     }
 
+    /** @hide */
     public NetworkStack() { }
 
     /**
      * Create a DHCP server according to the specified parameters.
      *
      * <p>The server will be returned asynchronously through the provided callbacks.
+     * @hide
      */
     public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
             final IDhcpServerCallbacks cb) {
@@ -88,6 +105,7 @@
      * Create an IpClient on the specified interface.
      *
      * <p>The IpClient will be returned asynchronously through the provided callbacks.
+     * @hide
      */
     public void makeIpClient(String ifName, IIpClientCallbacks cb) {
         requestConnector(connector -> {
@@ -103,6 +121,7 @@
      * Create a NetworkMonitor.
      *
      * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+     * @hide
      */
     public void makeNetworkMonitor(
             NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
@@ -153,6 +172,7 @@
      * the system server on devices that do not support the network stack module. The network stack
      * connector will then be delivered asynchronously to clients that requested it before it was
      * started.
+     * @hide
      */
     public void start(Context context) {
         mNetworkStackStartRequested = true;
@@ -223,7 +243,7 @@
     private void requestConnector(@NonNull NetworkStackCallback request) {
         // TODO: PID check.
         final int caller = Binder.getCallingUid();
-        if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
+        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
             // Don't even attempt to obtain the connector and give a nice error message
             throw new SecurityException(
                     "Only the system server should try to bind to the network stack.");
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index dc099a4..784f233 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -791,6 +791,27 @@
         }
 
         /**
+         * Marks the VPN network as metered. A VPN network is classified as metered when the user is
+         * sensitive to heavy data usage due to monetary costs and/or data limitations. In such
+         * cases, you should set this to {@code true} so that apps on the system can avoid doing
+         * large data transfers. Otherwise, set this to {@code false}. Doing so would cause VPN
+         * network to inherit its meteredness from its underlying networks.
+         *
+         * <p>VPN apps targeting {@link android.os.Build.VERSION_CODES#Q} or above will be
+         * considered metered by default.
+         *
+         * @param isMetered {@code true} if VPN network should be treated as metered regardless of
+         *     underlying network meteredness
+         * @return this {@link Builder} object to facilitate chaining method calls
+         * @see #setUnderlyingNetworks(Networks[])
+         * @see ConnectivityManager#isActiveNetworkMetered()
+         */
+        public Builder setMetered(boolean isMetered) {
+            mConfig.isMetered = isMetered;
+            return this;
+        }
+
+        /**
          * Create a VPN interface using the parameters supplied to this
          * builder. The interface works on IP packets, and a file descriptor
          * is returned for the application to access them. Each read
diff --git a/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml b/core/java/android/os/BatterySaverPolicyConfig.aidl
similarity index 60%
rename from packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
rename to core/java/android/os/BatterySaverPolicyConfig.aidl
index 4bac7da..37c66d0 100644
--- a/packages/overlays/FontRubikRubikOverlay/res/values/strings.xml
+++ b/core/java/android/os/BatterySaverPolicyConfig.aidl
@@ -1,12 +1,11 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open 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,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Headline / Body font Rubik overlay -->
-    <string name="font_rubik_rubik_overlay" translatable="false">Rubik / Rubik</string>
-</resources>
+
+package android.os;
+
+parcelable BatterySaverPolicyConfig;
diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java
new file mode 100644
index 0000000..b6e2b69
--- /dev/null
+++ b/core/java/android/os/BatterySaverPolicyConfig.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Config to set Battery Saver policy flags.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BatterySaverPolicyConfig implements Parcelable {
+    private final float mAdjustBrightnessFactor;
+    private final boolean mAdvertiseIsEnabled;
+    private final boolean mDeferFullBackup;
+    private final boolean mDeferKeyValueBackup;
+    @NonNull
+    private final Map<String, String> mDeviceSpecificSettings;
+    private final boolean mDisableAnimation;
+    private final boolean mDisableAod;
+    private final boolean mDisableLaunchBoost;
+    private final boolean mDisableOptionalSensors;
+    private final boolean mDisableSoundTrigger;
+    private final boolean mDisableVibration;
+    private final boolean mEnableAdjustBrightness;
+    private final boolean mEnableDataSaver;
+    private final boolean mEnableFirewall;
+    private final boolean mEnableQuickDoze;
+    private final boolean mForceAllAppsStandby;
+    private final boolean mForceBackgroundCheck;
+    private final int mGpsMode;
+
+    private BatterySaverPolicyConfig(Builder in) {
+        mAdjustBrightnessFactor = Math.max(0, Math.min(in.mAdjustBrightnessFactor, 1f));
+        mAdvertiseIsEnabled = in.mAdvertiseIsEnabled;
+        mDeferFullBackup = in.mDeferFullBackup;
+        mDeferKeyValueBackup = in.mDeferKeyValueBackup;
+        mDeviceSpecificSettings = Collections.unmodifiableMap(in.mDeviceSpecificSettings);
+        mDisableAnimation = in.mDisableAnimation;
+        mDisableAod = in.mDisableAod;
+        mDisableLaunchBoost = in.mDisableLaunchBoost;
+        mDisableOptionalSensors = in.mDisableOptionalSensors;
+        mDisableSoundTrigger = in.mDisableSoundTrigger;
+        mDisableVibration = in.mDisableVibration;
+        mEnableAdjustBrightness = in.mEnableAdjustBrightness;
+        mEnableDataSaver = in.mEnableDataSaver;
+        mEnableFirewall = in.mEnableFirewall;
+        mEnableQuickDoze = in.mEnableQuickDoze;
+        mForceAllAppsStandby = in.mForceAllAppsStandby;
+        mForceBackgroundCheck = in.mForceBackgroundCheck;
+        mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+                Math.min(in.mGpsMode, PowerManager.MAX_LOCATION_MODE));
+    }
+
+    private BatterySaverPolicyConfig(Parcel in) {
+        mAdjustBrightnessFactor = Math.max(0, Math.min(in.readFloat(), 1f));
+        mAdvertiseIsEnabled = in.readBoolean();
+        mDeferFullBackup = in.readBoolean();
+        mDeferKeyValueBackup = in.readBoolean();
+
+        final int size = in.readInt();
+        Map<String, String> deviceSpecificSettings = new ArrayMap<>(size);
+        for (int i = 0; i < size; ++i) {
+            String key = TextUtils.emptyIfNull(in.readString());
+            String val = TextUtils.emptyIfNull(in.readString());
+            if (key.trim().isEmpty()) {
+                continue;
+            }
+            deviceSpecificSettings.put(key, val);
+        }
+        mDeviceSpecificSettings = Collections.unmodifiableMap(deviceSpecificSettings);
+
+        mDisableAnimation = in.readBoolean();
+        mDisableAod = in.readBoolean();
+        mDisableLaunchBoost = in.readBoolean();
+        mDisableOptionalSensors = in.readBoolean();
+        mDisableSoundTrigger = in.readBoolean();
+        mDisableVibration = in.readBoolean();
+        mEnableAdjustBrightness = in.readBoolean();
+        mEnableDataSaver = in.readBoolean();
+        mEnableFirewall = in.readBoolean();
+        mEnableQuickDoze = in.readBoolean();
+        mForceAllAppsStandby = in.readBoolean();
+        mForceBackgroundCheck = in.readBoolean();
+        mGpsMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+                Math.min(in.readInt(), PowerManager.MAX_LOCATION_MODE));
+    }
+
+    public static final Creator<BatterySaverPolicyConfig> CREATOR =
+            new Creator<BatterySaverPolicyConfig>() {
+                @Override
+                public BatterySaverPolicyConfig createFromParcel(Parcel in) {
+                    return new BatterySaverPolicyConfig(in);
+                }
+
+                @Override
+                public BatterySaverPolicyConfig[] newArray(int size) {
+                    return new BatterySaverPolicyConfig[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(mAdjustBrightnessFactor);
+        dest.writeBoolean(mAdvertiseIsEnabled);
+        dest.writeBoolean(mDeferFullBackup);
+        dest.writeBoolean(mDeferKeyValueBackup);
+
+        final Set<Map.Entry<String, String>> entries = mDeviceSpecificSettings.entrySet();
+        final int size = entries.size();
+        dest.writeInt(size);
+        for (Map.Entry<String, String> entry : entries) {
+            dest.writeString(entry.getKey());
+            dest.writeString(entry.getValue());
+        }
+
+        dest.writeBoolean(mDisableAnimation);
+        dest.writeBoolean(mDisableAod);
+        dest.writeBoolean(mDisableLaunchBoost);
+        dest.writeBoolean(mDisableOptionalSensors);
+        dest.writeBoolean(mDisableSoundTrigger);
+        dest.writeBoolean(mDisableVibration);
+        dest.writeBoolean(mEnableAdjustBrightness);
+        dest.writeBoolean(mEnableDataSaver);
+        dest.writeBoolean(mEnableFirewall);
+        dest.writeBoolean(mEnableQuickDoze);
+        dest.writeBoolean(mForceAllAppsStandby);
+        dest.writeBoolean(mForceBackgroundCheck);
+        dest.writeInt(mGpsMode);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<String, String> entry : mDeviceSpecificSettings.entrySet()) {
+            sb.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
+        }
+        return "adjust_brightness_disabled=" + !mEnableAdjustBrightness + ","
+                + "adjust_brightness_factor=" + mAdjustBrightnessFactor + ","
+                + "advertise_is_enabled=" + mAdvertiseIsEnabled + ","
+                + "animation_disabled=" + mDisableAnimation + ","
+                + "aod_disabled=" + mDisableAod + ","
+                + "datasaver_disabled=" + !mEnableDataSaver + ","
+                + "firewall_disabled=" + !mEnableFirewall + ","
+                + "force_all_apps_standby=" + mForceAllAppsStandby + ","
+                + "force_background_check=" + mForceBackgroundCheck + ","
+                + "fullbackup_deferred=" + mDeferFullBackup + ","
+                + "gps_mode=" + mGpsMode + ","
+                + "keyvaluebackup_deferred=" + mDeferKeyValueBackup + ","
+                + "launch_boost_disabled=" + mDisableLaunchBoost + ","
+                + "optional_sensors_disabled=" + mDisableOptionalSensors + ","
+                + "quick_doze_enabled=" + mEnableQuickDoze + ","
+                + "soundtrigger_disabled=" + mDisableSoundTrigger + ","
+                + "vibration_disabled=" + mDisableVibration + ","
+                + sb.toString();
+    }
+
+    /**
+     * How much to adjust the screen brightness while in Battery Saver. This will have no effect
+     * if {@link #getEnableAdjustBrightness()} is {@code false}.
+     */
+    public float getAdjustBrightnessFactor() {
+        return mAdjustBrightnessFactor;
+    }
+
+    /**
+     * Whether or not to tell the system (and other apps) that Battery Saver is currently enabled.
+     */
+    public boolean getAdvertiseIsEnabled() {
+        return mAdvertiseIsEnabled;
+    }
+
+    /** Whether or not to defer full backup while in Battery Saver. */
+    public boolean getDeferFullBackup() {
+        return mDeferFullBackup;
+    }
+
+    /** Whether or not to defer key-value backup while in Battery Saver. */
+    public boolean getDeferKeyValueBackup() {
+        return mDeferKeyValueBackup;
+    }
+
+    /**
+     * Returns the device-specific battery saver constants.
+     */
+    @NonNull
+    public Map<String, String> getDeviceSpecificSettings() {
+        return mDeviceSpecificSettings;
+    }
+
+    /** Whether or not to disable animation while in Battery Saver. */
+    public boolean getDisableAnimation() {
+        return mDisableAnimation;
+    }
+
+    /** Whether or not to disable Always On Display while in Battery Saver. */
+    public boolean getDisableAod() {
+        return mDisableAod;
+    }
+
+    /** Whether or not to disable launch boost while in Battery Saver. */
+    public boolean getDisableLaunchBoost() {
+        return mDisableLaunchBoost;
+    }
+
+    /** Whether or not to disable optional sensors while in Battery Saver. */
+    public boolean getDisableOptionalSensors() {
+        return mDisableOptionalSensors;
+    }
+
+    /** Whether or not to disable sound trigger while in Battery Saver. */
+    public boolean getDisableSoundTrigger() {
+        return mDisableSoundTrigger;
+    }
+
+    /** Whether or not to disable vibration while in Battery Saver. */
+    public boolean getDisableVibration() {
+        return mDisableVibration;
+    }
+
+    /** Whether or not to enable brightness adjustment while in Battery Saver. */
+    public boolean getEnableAdjustBrightness() {
+        return mEnableAdjustBrightness;
+    }
+
+    /** Whether or not to enable Data Saver while in Battery Saver. */
+    public boolean getEnableDataSaver() {
+        return mEnableDataSaver;
+    }
+
+    /** Whether or not to enable the network firewall while in Battery Saver. */
+    public boolean getEnableFirewall() {
+        return mEnableFirewall;
+    }
+
+    /** Whether or not to enable Quick Doze while in Battery Saver. */
+    public boolean getEnableQuickDoze() {
+        return mEnableQuickDoze;
+    }
+
+    /** Whether or not to force all apps to standby mode while in Battery Saver. */
+    public boolean getForceAllAppsStandby() {
+        return mForceAllAppsStandby;
+    }
+
+    /** Whether or not to force background check while in Battery Saver. */
+    public boolean getForceBackgroundCheck() {
+        return mForceBackgroundCheck;
+    }
+
+    /** The GPS mode while in Battery Saver. */
+    public int getGpsMode() {
+        return mGpsMode;
+    }
+
+    /** Builder class for constructing {@link BatterySaverPolicyConfig} objects. */
+    public static final class Builder {
+        private float mAdjustBrightnessFactor = 1f;
+        private boolean mAdvertiseIsEnabled = false;
+        private boolean mDeferFullBackup = false;
+        private boolean mDeferKeyValueBackup = false;
+        @NonNull
+        private final ArrayMap<String, String> mDeviceSpecificSettings = new ArrayMap<>();
+        private boolean mDisableAnimation = false;
+        private boolean mDisableAod = false;
+        private boolean mDisableLaunchBoost = false;
+        private boolean mDisableOptionalSensors = false;
+        private boolean mDisableSoundTrigger = false;
+        private boolean mDisableVibration = false;
+        private boolean mEnableAdjustBrightness = false;
+        private boolean mEnableDataSaver = false;
+        private boolean mEnableFirewall = false;
+        private boolean mEnableQuickDoze = false;
+        private boolean mForceAllAppsStandby = false;
+        private boolean mForceBackgroundCheck = false;
+        private int mGpsMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+
+        public Builder() {
+        }
+
+        /**
+         * Set how much to adjust the screen brightness while in Battery Saver. The value should
+         * be in the [0, 1] range, where 1 will not change the brightness. This will have no
+         * effect if {@link #setEnableAdjustBrightness(boolean)} is not called with {@code true}.
+         */
+        @NonNull
+        public Builder setAdjustBrightnessFactor(float adjustBrightnessFactor) {
+            mAdjustBrightnessFactor = adjustBrightnessFactor;
+            return this;
+        }
+
+        /**
+         * Set whether or not to tell the system (and other apps) that Battery Saver is
+         * currently enabled.
+         */
+        @NonNull
+        public Builder setAdvertiseIsEnabled(boolean advertiseIsEnabled) {
+            mAdvertiseIsEnabled = advertiseIsEnabled;
+            return this;
+        }
+
+        /** Set whether or not to defer full backup while in Battery Saver. */
+        @NonNull
+        public Builder setDeferFullBackup(boolean deferFullBackup) {
+            mDeferFullBackup = deferFullBackup;
+            return this;
+        }
+
+        /** Set whether or not to defer key-value backup while in Battery Saver. */
+        @NonNull
+        public Builder setDeferKeyValueBackup(boolean deferKeyValueBackup) {
+            mDeferKeyValueBackup = deferKeyValueBackup;
+            return this;
+        }
+
+        /**
+         * Adds a key-value pair for device-specific battery saver constants. The supported keys
+         * and values are the same as those in
+         * {@link android.provider.Settings.Global#BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS}.
+         *
+         * @throws IllegalArgumentException if the provided key is invalid (empty, null, or all
+         * whitespace)
+         */
+        @NonNull
+        public Builder addDeviceSpecificSetting(@NonNull String key, @NonNull String value) {
+            if (key == null) {
+                throw new IllegalArgumentException("Key cannot be null");
+            }
+            key = key.trim();
+            if (TextUtils.isEmpty(key)) {
+                throw new IllegalArgumentException("Key cannot be empty");
+            }
+            mDeviceSpecificSettings.put(key, TextUtils.emptyIfNull(value));
+            return this;
+        }
+
+        /** Set whether or not to disable animation while in Battery Saver. */
+        @NonNull
+        public Builder setDisableAnimation(boolean disableAnimation) {
+            mDisableAnimation = disableAnimation;
+            return this;
+        }
+
+        /** Set whether or not to disable Always On Display while in Battery Saver. */
+        @NonNull
+        public Builder setDisableAod(boolean disableAod) {
+            mDisableAod = disableAod;
+            return this;
+        }
+
+        /** Set whether or not to disable launch boost while in Battery Saver. */
+        @NonNull
+        public Builder setDisableLaunchBoost(boolean disableLaunchBoost) {
+            mDisableLaunchBoost = disableLaunchBoost;
+            return this;
+        }
+
+        /** Set whether or not to disable optional sensors while in Battery Saver. */
+        @NonNull
+        public Builder setDisableOptionalSensors(boolean disableOptionalSensors) {
+            mDisableOptionalSensors = disableOptionalSensors;
+            return this;
+        }
+
+        /** Set whether or not to disable sound trigger while in Battery Saver. */
+        @NonNull
+        public Builder setDisableSoundTrigger(boolean disableSoundTrigger) {
+            mDisableSoundTrigger = disableSoundTrigger;
+            return this;
+        }
+
+        /** Set whether or not to disable vibration while in Battery Saver. */
+        @NonNull
+        public Builder setDisableVibration(boolean disableVibration) {
+            mDisableVibration = disableVibration;
+            return this;
+        }
+
+        /** Set whether or not to enable brightness adjustment while in Battery Saver. */
+        @NonNull
+        public Builder setEnableAdjustBrightness(boolean enableAdjustBrightness) {
+            mEnableAdjustBrightness = enableAdjustBrightness;
+            return this;
+        }
+
+        /** Set whether or not to enable Data Saver while in Battery Saver. */
+        @NonNull
+        public Builder setEnableDataSaver(boolean enableDataSaver) {
+            mEnableDataSaver = enableDataSaver;
+            return this;
+        }
+
+        /** Set whether or not to enable the network firewall while in Battery Saver. */
+        @NonNull
+        public Builder setEnableFirewall(boolean enableFirewall) {
+            mEnableFirewall = enableFirewall;
+            return this;
+        }
+
+        /** Set whether or not to enable Quick Doze while in Battery Saver. */
+        @NonNull
+        public Builder setEnableQuickDoze(boolean enableQuickDoze) {
+            mEnableQuickDoze = enableQuickDoze;
+            return this;
+        }
+
+        /** Set whether or not to force all apps to standby mode while in Battery Saver. */
+        @NonNull
+        public Builder setForceAllAppsStandby(boolean forceAllAppsStandby) {
+            mForceAllAppsStandby = forceAllAppsStandby;
+            return this;
+        }
+
+        /** Set whether or not to force background check while in Battery Saver. */
+        @NonNull
+        public Builder setForceBackgroundCheck(boolean forceBackgroundCheck) {
+            mForceBackgroundCheck = forceBackgroundCheck;
+            return this;
+        }
+
+        /** Set the GPS mode while in Battery Saver. */
+        @NonNull
+        public Builder setGpsMode(@PowerManager.LocationPowerSaveMode int gpsMode) {
+            mGpsMode = gpsMode;
+            return this;
+        }
+
+        /**
+         * Build a {@link BatterySaverPolicyConfig} object using the set parameters. This object
+         * is immutable.
+         */
+        @NonNull
+        public BatterySaverPolicyConfig build() {
+            if (!mEnableAdjustBrightness && Float.compare(1f, mAdjustBrightnessFactor) != 0) {
+                throw new IllegalArgumentException("Brightness adjustment factor changed without "
+                        + "enabling brightness adjustment");
+            }
+            return new BatterySaverPolicyConfig(this);
+        }
+    }
+}
diff --git a/core/java/android/os/DynamicAndroidManager.java b/core/java/android/os/DynamicAndroidManager.java
new file mode 100644
index 0000000..5238896
--- /dev/null
+++ b/core/java/android/os/DynamicAndroidManager.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.gsi.GsiProgress;
+
+/**
+ * The DynamicAndroidManager offers a mechanism to use a new Android image temporarily. After the
+ * installation, the device can reboot into this image with a new created /data. This image will
+ * last until the next reboot and then the device will go back to the original image. However the
+ * installed image and the new created /data are not deleted but disabled. Thus the application can
+ * either re-enable the installed image by calling {@link #toggle} or use the {@link #remove} to
+ * delete it completely. In other words, there are three device states: no installation, installed
+ * and running. The procedure to install a DynamicAndroid starts with a {@link #startInstallation},
+ * followed by a series of {@link #write} and ends with a {@link commit}. Once the installation is
+ * complete, the device state changes from no installation to the installed state and a followed
+ * reboot will change its state to running. Note one instance of dynamic android can exist on a
+ * given device thus the {@link #startInstallation} will fail if the device is currently running a
+ * DynamicAndroid.
+ *
+ * @hide
+ */
+@SystemService(Context.DYNAMIC_ANDROID_SERVICE)
+public class DynamicAndroidManager {
+    private static final String TAG = "DynamicAndroidManager";
+
+    private final IDynamicAndroidService mService;
+
+    /** {@hide} */
+    public DynamicAndroidManager(IDynamicAndroidService service) {
+        mService = service;
+    }
+
+    /** The DynamicAndroidManager.Session represents a started session for the installation. */
+    public class Session {
+        private Session() {}
+        /**
+         * Write a chunk of the DynamicAndroid system image
+         *
+         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
+         *     error.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+        public boolean write(byte[] buf) {
+            try {
+                return mService.write(buf);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+
+        /**
+         * Finish write and make device to boot into the it after reboot.
+         *
+         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
+         *     error.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+        public boolean commit() {
+            try {
+                return mService.commit();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+    }
+    /**
+     * Start DynamicAndroid installation. This call may take an unbounded amount of time. The caller
+     * may use another thread to call the getStartProgress() to get the progress.
+     *
+     * @param systemSize system size in bytes
+     * @param userdataSize userdata size in bytes
+     * @return {@code true} if the call succeeds. {@code false} either the device does not contain
+     *     enough space or a DynamicAndroid is currently in use where the {@link #isInUse} would be
+     *     true.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public Session startInstallation(long systemSize, long userdataSize) {
+        try {
+            if (mService.startInstallation(systemSize, userdataSize)) {
+                return new Session();
+            } else {
+                return null;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Query the progress of the current installation operation. This can be called while the
+     * installation is in progress.
+     *
+     * @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
+     *     status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
+     *     IGsiService.STATUS_COMPLETE.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public GsiProgress getInstallationProgress() {
+        try {
+            return mService.getInstallationProgress();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Abort the installation process. Note this method must be called in a thread other than the
+     * one calling the startInstallation method as the startInstallation method will not return
+     * until it is finished.
+     *
+     * @return {@code true} if the call succeeds. {@code false} if there is no installation
+     *     currently.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public boolean abort() {
+        try {
+            return mService.abort();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /** @return {@code true} if the device is running a dynamic android */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public boolean isInUse() {
+        try {
+            return mService.isInUse();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /** @return {@code true} if the device has a dynamic android installed */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public boolean isInstalled() {
+        try {
+            return mService.isInstalled();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Remove DynamicAndroid installation if present
+     *
+     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public boolean remove() {
+        try {
+            return mService.remove();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Enable DynamicAndroid when it's not enabled, otherwise, disable it.
+     *
+     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+    public boolean toggle() {
+        try {
+            return mService.toggle();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+}
diff --git a/core/java/android/os/IDynamicAndroidService.aidl b/core/java/android/os/IDynamicAndroidService.aidl
new file mode 100644
index 0000000..0b28799
--- /dev/null
+++ b/core/java/android/os/IDynamicAndroidService.aidl
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.gsi.GsiProgress;
+
+/** {@hide} */
+interface IDynamicAndroidService
+{
+    /**
+     * Start DynamicAndroid installation. This call may take 60~90 seconds. The caller
+     * may use another thread to call the getStartProgress() to get the progress.
+     *
+     * @param systemSize system size in bytes
+     * @param userdataSize userdata size in bytes
+     * @return true if the call succeeds
+     */
+    boolean startInstallation(long systemSize, long userdataSize);
+
+    /**
+     * Query the progress of the current installation operation. This can be called while
+     * the installation is in progress.
+     *
+     * @return GsiProgress
+     */
+    GsiProgress getInstallationProgress();
+
+    /**
+     * Abort the installation process. Note this method must be called in a thread other
+     * than the one calling the startInstallation method as the startInstallation
+     * method will not return until it is finished.
+     *
+     * @return true if the call succeeds
+     */
+    boolean abort();
+
+    /**
+     * @return true if the device is running an DynamicAnroid image
+     */
+    boolean isInUse();
+
+    /**
+     * @return true if the device has an DynamicAndroid image installed
+     */
+    boolean isInstalled();
+
+    /**
+     * Remove DynamicAndroid installation if present
+     *
+     * @return true if the call succeeds
+     */
+    boolean remove();
+
+    /**
+     * Enable DynamicAndroid when it's not enabled, otherwise, disable it.
+     *
+     * @return true if the call succeeds
+     */
+    boolean toggle();
+
+    /**
+     * Write a chunk of the DynamicAndroid system image
+     *
+     * @return true if the call succeeds
+     */
+    boolean write(in byte[] buf);
+
+    /**
+     * Finish write and make device to boot into the it after reboot.
+     *
+     * @return true if the call succeeds
+     */
+    boolean commit();
+}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index ca5b233..093897a 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -17,8 +17,9 @@
 
 package android.os;
 
-import android.os.WorkSource;
+import android.os.BatterySaverPolicyConfig;
 import android.os.PowerSaveState;
+import android.os.WorkSource;
 
 /** @hide */
 
@@ -49,6 +50,8 @@
     PowerSaveState getPowerSaveState(int serviceType);
     boolean setPowerSaveMode(boolean mode);
     boolean setDynamicPowerSavings(boolean dynamicPowerSavingsEnabled, int disableThreshold);
+    boolean setAdaptivePowerSavePolicy(in BatterySaverPolicyConfig config);
+    boolean setAdaptivePowerSaveEnabled(boolean enabled);
     int getPowerSaveMode();
     boolean isDeviceIdleMode();
     boolean isLightDeviceIdleMode();
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
new file mode 100644
index 0000000..b568f15
--- /dev/null
+++ b/core/java/android/os/OWNERS
@@ -0,0 +1,2 @@
+# Zygote
+per-file ZygoteProcess.java = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 7f4254e..0441ba2 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -640,6 +640,9 @@
      */
     public static final int LOCATION_MODE_FOREGROUND_ONLY = 3;
 
+    static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
+    static final int MAX_LOCATION_MODE = LOCATION_MODE_FOREGROUND_ONLY;
+
     /**
      * @hide
      */
@@ -1272,6 +1275,48 @@
     }
 
     /**
+     * Sets the policy for adaptive power save.
+     *
+     * @return true if there was an effectual change. If full battery saver is enabled or the
+     * adaptive policy is not enabled, then this will return false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setAdaptivePowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
+        try {
+            return mService.setAdaptivePowerSavePolicy(config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Enables or disables adaptive power save.
+     *
+     * @return true if there was an effectual change. If full battery saver is enabled, then this
+     * will return false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setAdaptivePowerSaveEnabled(boolean enabled) {
+        try {
+            return mService.setAdaptivePowerSaveEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Indicates automatic battery saver toggling by the system will be based on percentage.
      *
      * @see PowerManager#getPowerSaveMode()
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index ad8a4d5..40c48a0 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -226,6 +226,7 @@
      * @hide
      */
     @TestApi
+    @SystemApi
     public static @AppIdInt int getAppId(int uid) {
         return uid % PER_USER_RANGE;
     }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 30c0731..92650e1 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -181,10 +181,10 @@
         String NAMESPACE = "intelligence_attention";
 
         /** If {@code true}, enables the attention features. */
-        String PROPERTY_ATTENTION_ENABLED = "attention_enabled";
+        String ATTENTION_ENABLED = "attention_enabled";
 
         /** Settings for the attention features. */
-        String PROPERTY_ATTENTION_SETTINGS = "attention_settings";
+        String ATTENTION_SETTINGS = "attention_settings";
     }
 
     /**
@@ -203,12 +203,12 @@
          * @hide
          */
         @SystemApi
-        String PROPERTY_PERMISSIONS_HUB_ENABLED = "enable_permissions_hub";
+        String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
 
         /**
          * Whether to show location access check notifications.
          */
-        String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "enable_location_access_check";
+        String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
     }
 
     /**
@@ -220,13 +220,17 @@
     public interface Telephony {
         String NAMESPACE = "telephony";
         /**
-         * Whether to apply ramping ringer on incoming phone calls.
-         */
-        String PROPERTY_ENABLE_RAMPING_RINGER = "enable_ramping_ringer";
-        /**
          * Ringer ramping time in milliseconds.
          */
-        String PROPERTY_RAMPING_RINGER_DURATION = "ramping_duration";
+        String RAMPING_RINGER_DURATION = "ramping_ringer_duration";
+        /**
+         * Whether to apply ramping ringer on incoming phone calls.
+         */
+        String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
+        /**
+         * Vibration time in milliseconds before ramping ringer starts.
+         */
+        String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration";
     }
 
     /**
@@ -261,6 +265,7 @@
         String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
         String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
         String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
+        String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate";
 
         /**
          * Maximum number of cached processes. See
@@ -279,10 +284,10 @@
         String NAMESPACE = "attention_manager_service";
 
         /** If {@code true}, enables {@link AttentionManagerService} features. */
-        String PROPERTY_SERVICE_ENABLED = "service_enabled";
+        String SERVICE_ENABLED = "service_enabled";
 
         /** Allows a CTS to inject a fake implementation. */
-        String PROPERTY_COMPONENT_NAME = "component_name";
+        String COMPONENT_NAME = "component_name";
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 124c50a..0743c23 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -641,6 +641,8 @@
          * location. For example, when this value is left undefined, pending
          * {@link MediaStore.Audio.Media} items are stored under
          * {@link Environment#DIRECTORY_MUSIC}.
+         *
+         * @see MediaColumns#PRIMARY_DIRECTORY
          */
         public void setPrimaryDirectory(@Nullable String primaryDirectory) {
             this.primaryDirectory = primaryDirectory;
@@ -652,6 +654,8 @@
          * <p>
          * You may leave this value undefined to store the media as a direct
          * descendant of the {@link #setPrimaryDirectory(String)} location.
+         *
+         * @see MediaColumns#SECONDARY_DIRECTORY
          */
         public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
             this.secondaryDirectory = secondaryDirectory;
@@ -980,6 +984,26 @@
          * Type: TEXT
          */
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
+
+        /**
+         * The primary directory name this media exists under. The value may be
+         * {@code NULL} if the media doesn't have a primary directory name.
+         * <p>
+         * Type: TEXT
+         *
+         * @see PendingParams#setPrimaryDirectory(String)
+         */
+        public static final String PRIMARY_DIRECTORY = "primary_directory";
+
+        /**
+         * The secondary directory name this media exists under. The value may
+         * be {@code NULL} if the media doesn't have a secondary directory name.
+         * <p>
+         * Type: TEXT
+         *
+         * @see PendingParams#setSecondaryDirectory(String)
+         */
+        public static final String SECONDARY_DIRECTORY = "secondary_directory";
     }
 
     /**
@@ -1428,13 +1452,20 @@
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
-             * The secondary bucket ID of this media item. This can be useful to
-             * present the user a second-level clustering of related media
-             * items. This is a read-only column that is automatically computed.
+             * The group ID of this media item. This can be useful to present
+             * the user a grouping of related media items, such a burst of
+             * images, or a {@code JPG} and {@code DNG} version of the same
+             * image.
+             * <p>
+             * This is a read-only column that is automatically computed based
+             * on the first portion of the filename. For example,
+             * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
+             * will have the same {@link #GROUP_ID} because the first portion of
+             * their filenames is identical.
              * <p>
              * Type: INTEGER
              */
-            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
+            public static final String GROUP_ID = "group_id";
         }
 
         public static final class Media implements ImageColumns {
@@ -2697,13 +2728,20 @@
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
-             * The secondary bucket ID of this media item. This can be useful to
-             * present the user a second-level clustering of related media
-             * items. This is a read-only column that is automatically computed.
+             * The group ID of this media item. This can be useful to present
+             * the user a grouping of related media items, such a burst of
+             * images, or a {@code JPG} and {@code DNG} version of the same
+             * image.
+             * <p>
+             * This is a read-only column that is automatically computed based
+             * on the first portion of the filename. For example,
+             * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
+             * will have the same {@link #GROUP_ID} because the first portion of
+             * their filenames is identical.
              * <p>
              * Type: INTEGER
              */
-            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
+            public static final String GROUP_ID = "group_id";
 
             /**
              * The bookmark for the video. Time in ms. Represents the location in the video that the
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5151e29..e14bb66 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3536,6 +3536,16 @@
         private static final Validator MASTER_MONO_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Master balance (float -1.f = 100% left, 0.f = dead center, 1.f = 100% right).
+         *
+         * @hide
+         */
+        public static final String MASTER_BALANCE = "master_balance";
+
+        private static final Validator MASTER_BALANCE_VALIDATOR =
+                new SettingsValidators.InclusiveFloatRangeValidator(-1.f, 1.f);
+
+        /**
          * Whether the notifications should use the ring volume (value of 1) or
          * a separate notification volume (value of 0). In most cases, users
          * will have this enabled so the notification and ringer volumes will be
@@ -4288,6 +4298,7 @@
             HEARING_AID,
             TTY_MODE,
             MASTER_MONO,
+            MASTER_BALANCE,
             SOUND_EFFECTS_ENABLED,
             HAPTIC_FEEDBACK_ENABLED,
             POWER_SOUNDS_ENABLED,       // moved to global
@@ -4395,6 +4406,7 @@
             PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
             PRIVATE_SETTINGS.add(VOLUME_MASTER);
             PRIVATE_SETTINGS.add(MASTER_MONO);
+            PRIVATE_SETTINGS.add(MASTER_BALANCE);
             PRIVATE_SETTINGS.add(NOTIFICATIONS_USE_RING_VOLUME);
             PRIVATE_SETTINGS.add(VIBRATE_IN_SILENT);
             PRIVATE_SETTINGS.add(MEDIA_BUTTON_RECEIVER);
@@ -4488,6 +4500,7 @@
             VALIDATORS.put(SCREEN_AUTO_BRIGHTNESS_ADJ, SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR);
             VALIDATORS.put(VIBRATE_INPUT_DEVICES, VIBRATE_INPUT_DEVICES_VALIDATOR);
             VALIDATORS.put(MASTER_MONO, MASTER_MONO_VALIDATOR);
+            VALIDATORS.put(MASTER_BALANCE, MASTER_BALANCE_VALIDATOR);
             VALIDATORS.put(NOTIFICATIONS_USE_RING_VOLUME, NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR);
             VALIDATORS.put(VIBRATE_IN_SILENT, VIBRATE_IN_SILENT_VALIDATOR);
             VALIDATORS.put(MEDIA_BUTTON_RECEIVER, MEDIA_BUTTON_RECEIVER_VALIDATOR);
@@ -5874,30 +5887,24 @@
                 "unknown_sources_default_reversed";
 
         /**
-         * Comma-separated list of location providers that activities may access. Do not rely on
-         * this value being present in settings.db or on ContentObserver notifications on the
+         * Comma-separated list of location providers that are accessible. Do not rely on
+         * this value being present or correct, or on ContentObserver notifications on the
          * corresponding Uri.
          *
-         * @deprecated Providers should not be controlled individually. See {@link #LOCATION_MODE}
-          * documentation for information on reading/writing location information.
+         * @deprecated The preferred methods for checking provider status and listening for changes
+         * are via {@link LocationManager#isProviderEnabled(String)} and
+         * {@link LocationManager#PROVIDERS_CHANGED_ACTION}.
          */
         @Deprecated
         public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
 
         /**
-         * The degree of location access enabled by the user.
-         * <p>
-         * When used with {@link #putInt(ContentResolver, String, int)}, must be one of {@link
-         * #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY}, {@link
-         * #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}. When used with {@link
-         * #getInt(ContentResolver, String)}, the caller must gracefully handle additional location
-         * modes that might be added in the future.
-         * <p>
-         * Note: do not rely on this value being present in settings.db or on ContentObserver
-         * notifications for the corresponding Uri. Use {@link LocationManager#MODE_CHANGED_ACTION}
-         * to receive changes in this value.
+         * The current location mode of the device. Do not rely on this value being present or on
+         * ContentObserver notifications on the corresponding Uri.
          *
-         * @deprecated To check location mode, use {@link LocationManager#isLocationEnabled()}.
+         * @deprecated The preferred methods for checking location mode and listening for changes
+         * are via {@link LocationManager#isLocationEnabled()} and
+         * {@link LocationManager#MODE_CHANGED_ACTION}.
          */
         @Deprecated
         public static final String LOCATION_MODE = "location_mode";
@@ -5924,7 +5931,7 @@
         public static final int LOCATION_CHANGER_QUICK_SETTINGS = 2;
 
         /**
-         * Location access disabled.
+         * Location mode is off.
          *
          * @deprecated See {@link #LOCATION_MODE}.
          */
@@ -5932,32 +5939,39 @@
         public static final int LOCATION_MODE_OFF = 0;
 
         /**
-         * Network Location Provider disabled, but GPS and other sensors enabled.
+         * This mode no longer has any distinct meaning, but is interpreted as the location mode is
+         * on.
          *
-         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
-         *             get the status of a location provider, use
-         *             {@link LocationManager#isProviderEnabled(String)}.
+         * @deprecated See {@link #LOCATION_MODE_ON}.
          */
         @Deprecated
         public static final int LOCATION_MODE_SENSORS_ONLY = 1;
 
         /**
-         * Reduced power usage, such as limiting the number of GPS updates per hour. Requests
-         * with {@link android.location.Criteria#POWER_HIGH} may be downgraded to
-         * {@link android.location.Criteria#POWER_MEDIUM}.
+         * This mode no longer has any distinct meaning, but is interpreted as the location mode is
+         * on.
          *
-         * @deprecated See {@link #LOCATION_MODE}.
+         * @deprecated See {@link #LOCATION_MODE_ON}.
          */
         @Deprecated
         public static final int LOCATION_MODE_BATTERY_SAVING = 2;
 
         /**
-         * Best-effort location computation allowed.
+         * This mode no longer has any distinct meaning, but is interpreted as the location mode is
+         * on.
+         *
+         * @deprecated See {@link #LOCATION_MODE_ON}.
+         */
+        @Deprecated
+        public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
+
+        /**
+         * Location mode is on.
          *
          * @deprecated See {@link #LOCATION_MODE}.
          */
         @Deprecated
-        public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
+        public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY;
 
         /**
          * A flag containing settings used for biometric weak
@@ -7466,14 +7480,6 @@
         private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Gesture that wakes up the lock screen.
-         * @hide
-         */
-        public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_lock_screen_gesture";
-
-        private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
-
-        /**
          * Gesture that wakes up the display, showing the ambient version of the status bar.
          * @hide
          */
@@ -8588,7 +8594,6 @@
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
             DOZE_TAP_SCREEN_GESTURE,
-            DOZE_WAKE_LOCK_SCREEN_GESTURE,
             DOZE_WAKE_SCREEN_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -8748,7 +8753,6 @@
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR);
-            VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
             VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -10600,6 +10604,18 @@
         private static final Validator WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR =
                 BOOLEAN_VALIDATOR;
 
+        /**
+         * Setting to enable the Wi-Fi link probing.
+         * Disabled by default, and setting it to 1 will enable it.
+         * The value is boolean (0 or 1).
+         * @hide
+         */
+        public static final String WIFI_LINK_PROBING_ENABLED =
+                "wifi_link_probing_enabled";
+
+        private static final Validator WIFI_LINK_PROBING_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
        /**
         * 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.
@@ -11425,6 +11441,9 @@
          * The following keys are supported:
          *
          * <pre>
+         * advertise_is_enabled              (boolean)
+         * datasaver_disabled                (boolean)
+         * launch_boost_disabled             (boolean)
          * vibration_disabled                (boolean)
          * animation_disabled                (boolean)
          * soundtrigger_disabled             (boolean)
@@ -11448,6 +11467,14 @@
         /**
          * Battery Saver device specific settings
          * This is encoded as a key=value list, separated by commas.
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         *     cpufreq-i (list of "core-number:frequency" pairs concatenated with /)
+         *     cpufreq-n (list of "core-number:frequency" pairs concatenated with /)
+         * </pre>
+         *
          * See {@link com.android.server.power.batterysaver.BatterySaverPolicy} for the details.
          *
          * @hide
@@ -11456,6 +11483,24 @@
                 "battery_saver_device_specific_constants";
 
         /**
+         * Settings for adaptive Battery Saver mode. Uses the same flags as
+         * {@link #BATTERY_SAVER_CONSTANTS}.
+         *
+         * @hide
+         */
+        public static final String BATTERY_SAVER_ADAPTIVE_CONSTANTS =
+                "battery_saver_adaptive_constants";
+
+        /**
+         * Device specific settings for adaptive Battery Saver mode. Uses the same flags as
+         * {@link #BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS}.
+         *
+         * @hide
+         */
+        public static final String BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS =
+                "battery_saver_adaptive_device_specific_constants";
+
+        /**
          * Battery tip specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -13393,6 +13438,7 @@
                     WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
             VALIDATORS.put(WIFI_PNO_RECENCY_SORTING_ENABLED,
                     WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR);
+            VALIDATORS.put(WIFI_LINK_PROBING_ENABLED, WIFI_LINK_PROBING_ENABLED_VALIDATOR);
         }
 
         /**
@@ -14282,6 +14328,19 @@
         public static final String LOOPER_STATS = "looper_stats";
 
         /**
+         * Settings for collecting statistics on CPU usage per thread
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     num_buckets          (int)
+         *     collected_uids       (string)
+         * </pre>
+         *
+         * @hide
+         */
+        public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
+
+        /**
          * Default user id to boot into. They map to user ids, for example, 10, 11, 12.
          *
          * @hide
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
index f6e448dc..24d74ff 100644
--- a/core/java/android/service/attention/AttentionService.java
+++ b/core/java/android/service/attention/AttentionService.java
@@ -64,10 +64,10 @@
     /** Attention is present. */
     public static final int ATTENTION_SUCCESS_PRESENT = 1;
 
-    /** Preempted by other camera user. */
+    /** Preempted by other client. */
     public static final int ATTENTION_FAILURE_PREEMPTED = 2;
 
-    /** Preempted by other camera user. */
+    /** Request timed out. */
     public static final int ATTENTION_FAILURE_TIMED_OUT = 3;
 
     /** Unknown reasons for failing to determine the attention. */
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 0da8039..333f4be 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -31,6 +31,7 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
+import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -62,7 +63,8 @@
             mHandler.sendMessage(
                     obtainMessage(ContentSuggestionsService::processContextImage,
                             ContentSuggestionsService.this, taskId,
-                            Bitmap.createHardwareBitmap(contextImage),
+                            Bitmap.wrapHardwareBuffer(
+                                    HardwareBuffer.createFromGraphicBuffer(contextImage), null),
                             imageContextRequestExtras));
         }
 
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
index 3c9bbf8..426f002 100644
--- a/core/java/android/service/dreams/OWNERS
+++ b/core/java/android/service/dreams/OWNERS
@@ -1,3 +1,3 @@
-dsandler@google.com
+dsandler@android.com
 michaelwr@google.com
 roosa@google.com
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 2789651..e76e096 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,7 +16,6 @@
 
 package android.service.voice;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -41,8 +40,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -80,33 +77,6 @@
      */
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"VOICE_STATE_"}, value = {
-            VOICE_STATE_NONE,
-            VOICE_STATE_CONDITIONAL_LISTENING,
-            VOICE_STATE_LISTENING,
-            VOICE_STATE_FULFILLING})
-    public @interface VoiceState {
-    }
-
-    /**
-     * Voice assistant inactive.
-     */
-    public static final int VOICE_STATE_NONE = 0;
-    /**
-     * Voice assistant listening, but will only trigger if it hears a request it can fulfill.
-     */
-    public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1;
-    /**
-     * Voice assistant is listening to user speech.
-     */
-    public static final int VOICE_STATE_LISTENING = 2;
-    /**
-     * Voice assistant is fulfilling an action requested by the user.
-     */
-    public static final int VOICE_STATE_FULFILLING = 3;
-
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
         @Override
         public void ready() {
@@ -376,7 +346,7 @@
      *
      * @param state value indicating whether the assistant is listening, fulfilling, etc.
      */
-    public final void setVoiceState(@VoiceState int state) {
+    public final void setVoiceState(int state) {
         try {
             mSystemService.setVoiceState(state);
         } catch (RemoteException e) {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index db9351b..1ca6398 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,6 +36,7 @@
     public static final String FFLAG_PREFIX = "sys.fflag.";
     public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
     public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
+    public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String SAFETY_HUB = "settings_safety_hub";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
@@ -51,12 +52,13 @@
         DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
-        DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
         DEFAULT_FLAGS.put("settings_slice_injection", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_wifi_dpp", "true");
         DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true");
         DEFAULT_FLAGS.put("settings_wifi_sharing", "true");
+        DEFAULT_FLAGS.put("settings_mainline_module", "false");
+        DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(SAFETY_HUB, "false");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 7d9ec70..c794a69 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -237,16 +237,6 @@
     private static final int LONG_PRESS = 2;
     private static final int TAP = 3;
 
-    /**
-     * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
-     * scrolling, will be inhibited. However, to account for the possibility of incorrect
-     * classification, the default scrolling will only be inhibited if the gesture moves beyond
-     * (default touch slop * AMBIGUOUS_GESTURE_MULTIPLIER). Likewise, the default long press
-     * timeout will be increased for some situations where the default behaviour
-     * is to cancel it.
-     */
-    private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
-
     private final Handler mHandler;
     @UnsupportedAppUsage
     private final OnGestureListener mListener;
@@ -636,6 +626,7 @@
                             hasPendingLongPress && ambiguousGesture;
                     if (shouldInhibitDefaultAction) {
                         // Inhibit default long press
+                        final float multiplier = ViewConfiguration.getAmbiguousGestureMultiplier();
                         if (distance > slopSquare) {
                             // The default action here is to remove long press. But if the touch
                             // slop below gets increased, and we never exceed the modified touch
@@ -643,15 +634,15 @@
                             // will happen in response to user input. To prevent this,
                             // reschedule long press with a modified timeout.
                             mHandler.removeMessages(LONG_PRESS);
-                            final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
+                            final long longPressTimeout = ViewConfiguration.getLongPressTimeout();
                             mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
-                                    + longPressTimeout * AMBIGUOUS_GESTURE_MULTIPLIER);
+                                    + (long) (longPressTimeout * multiplier));
                         }
                         // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
                         // until the gesture is resolved.
                         // However, for safety, simply increase the touch slop in case the
                         // classification is erroneous. Since the value is squared, multiply twice.
-                        slopSquare *= AMBIGUOUS_GESTURE_MULTIPLIER * AMBIGUOUS_GESTURE_MULTIPLIER;
+                        slopSquare *= multiplier * multiplier;
                     }
 
                     if (distance > slopSquare) {
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 6061cb2..6a290b7 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -414,8 +414,7 @@
 
         // Make sure the application allows code generation
         ApplicationInfo appInfo = mContext.getApplicationInfo();
-        if ((appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
-            || appInfo.isPrivilegedApp()) {
+        if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) {
             mUseCompiledView = false;
             return;
         }
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 9d3552f..b6a4a09 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -431,8 +431,9 @@
 
     /**
      * This flag indicates that the window that received this motion event is partly
-     * or wholly obscured by another visible window above it.  This flag is set to true
-     * even if the event did not directly pass through the obscured area.
+     * or wholly obscured by another visible window above it. This flag is set to true
+     * if the event directly passed through the obscured area.
+     *
      * A security sensitive application can check this flag to identify situations in which
      * a malicious application may have covered up part of its content for the purpose
      * of misleading the user or hijacking touches.  An appropriate response might be
@@ -443,16 +444,17 @@
 
     /**
      * This flag indicates that the window that received this motion event is partly
-     * or wholly obscured by another visible window above it.  This flag is set to true
+     * or wholly obscured by another visible window above it. This flag is set to true
      * even if the event did not directly pass through the obscured area.
+     *
      * A security sensitive application can check this flag to identify situations in which
      * a malicious application may have covered up part of its content for the purpose
      * of misleading the user or hijacking touches.  An appropriate response might be
      * to drop the suspect touches or to take additional precautions to confirm the user's
      * actual intent.
      *
-     * Unlike FLAG_WINDOW_IS_OBSCURED, this is actually true.
-     * @hide
+     * Unlike FLAG_WINDOW_IS_OBSCURED, this is true even if the window that received this event is
+     * obstructed in areas other than the touched location.
      */
     public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2956606..8061cc3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.os.IBinder;
@@ -154,6 +155,8 @@
     private static native int nativeGetActiveConfig(IBinder displayToken);
     private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
+    private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+            IBinder displayToken);
     private static native int[] nativeGetCompositionDataspaces();
     private static native int nativeGetActiveColorMode(IBinder displayToken);
     private static native boolean nativeSetActiveColorMode(IBinder displayToken,
@@ -501,16 +504,7 @@
             }
             mWidth = width;
             mHeight = height;
-            // set this as a buffer layer since we are specifying a buffer size.
-            return setFlags(FX_SURFACE_NORMAL, FX_SURFACE_MASK);
-        }
-
-        /**
-         * Set the initial size of the controlled surface's buffers in pixels.
-         */
-        private void unsetBufferSize() {
-            mWidth = 0;
-            mHeight = 0;
+            return this;
         }
 
         /**
@@ -628,11 +622,16 @@
          * Color layers will not have an associated BufferQueue and will instead always render a
          * solid color (that is, solid before plane alpha). Currently that color is black.
          *
+         * @param isColorLayer Whether to create a color layer.
          * @hide
          */
-        public Builder setColorLayer() {
-            unsetBufferSize();
-            return setFlags(FX_SURFACE_DIM, FX_SURFACE_MASK);
+        public Builder setColorLayer(boolean isColorLayer) {
+            if (isColorLayer) {
+                mFlags |= FX_SURFACE_DIM;
+            } else {
+                mFlags &= ~FX_SURFACE_DIM;
+            }
+            return this;
         }
 
         private boolean isColorLayerSet() {
@@ -645,11 +644,16 @@
          * Container layers will not be rendered in any fashion and instead are used
          * as a parent of renderable layers.
          *
+         * @param isContainerLayer Whether to create a container layer.
          * @hide
          */
-        public Builder setContainerLayer() {
-            unsetBufferSize();
-            return setFlags(FX_SURFACE_CONTAINER, FX_SURFACE_MASK);
+        public Builder setContainerLayer(boolean isContainerLayer) {
+            if (isContainerLayer) {
+                mFlags |= FX_SURFACE_CONTAINER;
+            } else {
+                mFlags &= ~FX_SURFACE_CONTAINER;
+            }
+            return this;
         }
 
         private boolean isContainerLayerSet() {
@@ -657,7 +661,7 @@
         }
 
         /**
-         * Set 'Surface creation flags' such as {@link #HIDDEN}, {@link #SECURE}.
+         * Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}.
          *
          * TODO: Finish conversion to individual builder methods?
          * @param flags The combined flags
@@ -667,11 +671,6 @@
             mFlags = flags;
             return this;
         }
-
-        private Builder setFlags(int flags, int mask) {
-            mFlags = (mFlags & ~mask) | flags;
-            return this;
-        }
     }
 
     /**
@@ -1539,6 +1538,73 @@
     }
 
     /**
+     * Color coordinates in CIE1931 XYZ color space
+     *
+     * @hide
+     */
+    public static final class CieXyz {
+        /**
+         * @hide
+         */
+        public float X;
+
+        /**
+         * @hide
+         */
+        public float Y;
+
+        /**
+         * @hide
+         */
+        public float Z;
+    }
+
+    /**
+     * Contains a display's color primaries
+     *
+     * @hide
+     */
+    public static final class DisplayPrimaries {
+        /**
+         * @hide
+         */
+        public CieXyz red;
+
+        /**
+         * @hide
+         */
+        public CieXyz green;
+
+        /**
+         * @hide
+         */
+        public CieXyz blue;
+
+        /**
+         * @hide
+         */
+        public CieXyz white;
+
+        /**
+         * @hide
+         */
+        public DisplayPrimaries() {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+            IBinder displayToken) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+
+        return nativeGetDisplayNativePrimaries(displayToken);
+    }
+
+    /**
      * @hide
      */
     public static int getActiveColorMode(IBinder displayToken) {
@@ -1738,7 +1804,10 @@
             Log.w(TAG, "Failed to take screenshot");
             return null;
         }
-        return Bitmap.createHardwareBitmap(buffer);
+        // TODO(b/116112787) Now that hardware bitmap creation can take color space, we
+        // should continue to fix screenshot.
+        return Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer),
+                ColorSpace.get(ColorSpace.Named.SRGB));
     }
 
     /**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ecb2ac4..ecbec65 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@
 import android.graphics.RenderNode;
 import android.os.Build;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -587,7 +588,7 @@
                     mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
                         .setName("Background for -" + name)
                         .setOpaque(true)
-                        .setColorLayer()
+                        .setColorLayer(true)
                         .setParent(mSurfaceControl)
                         .build();
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 519181d..992b996 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.content.res.Resources.ID_NULL;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
 
@@ -3985,15 +3986,6 @@
     public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
 
     /**
-     * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain the default
-     * long press action will be inhibited. However, to account for the possibility of incorrect
-     * classification, the default long press timeout will instead be increased for some situations
-     * by the following factor.
-     * Likewise, the touch slop for allowing long press will be increased when gesture is uncertain.
-     */
-    private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
-
-    /**
      * Controls the over-scroll mode for this view.
      * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
      * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -5052,6 +5044,9 @@
     @Nullable
     private WeakReference<ContentCaptureSession> mContentCaptureSession;
 
+    @LayoutRes
+    private int mSourceLayoutId = ID_NULL;
+
     /**
      * Simple constructor to use when creating a view from code.
      *
@@ -5217,6 +5212,8 @@
     public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         this(context);
 
+        mSourceLayoutId = Resources.getAttributeSetSourceResId(attrs);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
 
@@ -14801,18 +14798,20 @@
                             motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
                     int touchSlop = mTouchSlop;
                     if (ambiguousGesture && hasPendingLongPressCallback()) {
+                        final float ambiguousMultiplier =
+                                ViewConfiguration.getAmbiguousGestureMultiplier();
                         if (!pointInView(x, y, touchSlop)) {
                             // The default action here is to cancel long press. But instead, we
                             // just extend the timeout here, in case the classification
                             // stays ambiguous.
                             removeLongPressCallback();
-                            long delay = ViewConfiguration.getLongPressTimeout()
-                                    * AMBIGUOUS_GESTURE_MULTIPLIER;
+                            long delay = (long) (ViewConfiguration.getLongPressTimeout()
+                                    * ambiguousMultiplier);
                             // Subtract the time already spent
                             delay -= event.getEventTime() - event.getDownTime();
                             checkForLongClick(delay, x, y);
                         }
-                        touchSlop *= AMBIGUOUS_GESTURE_MULTIPLIER;
+                        touchSlop *= ambiguousMultiplier;
                     }
 
                     // Be lenient about moving outside of buttons
@@ -23253,6 +23252,18 @@
     }
 
     /**
+     * A {@link View} can be inflated from an XML layout. For such Views this method returns the
+     * resource ID of the source layout.
+     *
+     * @return The layout resource id if this view was inflated from XML, otherwise
+     * {@link Resources#ID_NULL}.
+     */
+    @LayoutRes
+    public int getSourceLayoutResId() {
+        return mSourceLayoutId;
+    }
+
+    /**
      * Returns the top padding of this view.
      *
      * @return the top padding in pixels
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index d03d97e..94d1b6d 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.FloatRange;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.AppGlobals;
@@ -286,6 +287,11 @@
     private static final int HAS_PERMANENT_MENU_KEY_TRUE = 1;
     private static final int HAS_PERMANENT_MENU_KEY_FALSE = 2;
 
+    /**
+     * The multiplication factor for inhibiting default gestures.
+     */
+    private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f;
+
     private final int mEdgeSlop;
     private final int mFadingEdgeLength;
     private final int mMinimumFlingVelocity;
@@ -911,6 +917,22 @@
     }
 
     /**
+     * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
+     * scrolling, will be inhibited.
+     * However, to account for the possibility of incorrect classification,
+     * the default scrolling will only be inhibited if the pointer travels less than
+     * (getScaledTouchSlop() * this factor).
+     * Likewise, the default long press timeout will be increased by this factor for some situations
+     * where the default behaviour is to cancel it.
+     *
+     * @return The multiplication factor for inhibiting default gestures.
+     */
+    @FloatRange(from = 1.0)
+    public static float getAmbiguousGestureMultiplier() {
+        return AMBIGUOUS_GESTURE_MULTIPLIER;
+    }
+
+    /**
      * Report if the device has a permanent menu key available to the user.
      *
      * <p>As of Android 3.0, devices may not have a permanent menu key available.
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 1928613..5814759 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.Display;
 import android.view.View;
 
 import com.android.internal.util.Preconditions;
@@ -90,8 +91,8 @@
     // Fields below are set by server when the session starts
     private final @Nullable ComponentName mComponentName;
     private final int mTaskId;
-    private final int mDisplayId;
     private final int mFlags;
+    private final int mDisplayId;
 
     // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
     private @Nullable String mParentSessionId;
@@ -123,7 +124,8 @@
         mAction = builder.mAction;
 
         mComponentName  = null;
-        mTaskId = mFlags = mDisplayId = 0;
+        mTaskId = mFlags = 0;
+        mDisplayId = Display.INVALID_DISPLAY;
     }
 
     /**
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index fde0ced..f31856c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -67,9 +67,6 @@
 
     private final Object mLock = new Object();
 
-    @GuardedBy("mLock")
-    private boolean mDisabled;
-
     @NonNull
     private final Context mContext;
 
@@ -115,8 +112,7 @@
     public MainContentCaptureSession getMainContentCaptureSession() {
         synchronized (mLock) {
             if (mMainSession == null) {
-                mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
-                        mDisabled);
+                mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
                 if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
             }
             return mMainSession;
@@ -180,9 +176,17 @@
      * </ul>
      */
     public boolean isContentCaptureEnabled() {
+        if (mService == null) return false;
+
+        final MainContentCaptureSession mainSession;
         synchronized (mLock) {
-            return mService != null && !mDisabled;
+            mainSession = mMainSession;
         }
+        // The main session is only set when the activity starts, so we need to return true until
+        // then.
+        if (mainSession != null && mainSession.isDisabled()) return false;
+
+        return true;
     }
 
     /**
@@ -287,7 +291,8 @@
     public void dump(String prefix, PrintWriter pw) {
         synchronized (mLock) {
             pw.print(prefix); pw.println("ContentCaptureManager");
-            pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+            pw.print(prefix); pw.print("isContentCaptureEnabled(): ");
+            pw.println(isContentCaptureEnabled());
             pw.print(prefix); pw.print("Context: "); pw.println(mContext);
             pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
             if (mService != null) {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2eca51f..034c8fa 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -89,21 +89,23 @@
      */
     public static final String EXTRA_BINDER = "binder";
 
-    // TODO(b/111276913): make sure disabled state is in sync with manager's disabled
     @NonNull
-    private final AtomicBoolean mDisabled;
+    private final AtomicBoolean mDisabled = new AtomicBoolean(false);
 
     @NonNull
     private final Context mContext;
 
     @NonNull
+    private final ContentCaptureManager mManager;
+
+    @NonNull
     private final Handler mHandler;
 
     /**
      * Interface to the system_server binder object - it's only used to start the session (and
      * notify when the session is finished).
      */
-    @Nullable
+    @Nullable // TODO(b/122959591): shoul never be null, we should make main session null instead
     private final IContentCaptureManager mSystemServerInterface;
 
     /**
@@ -136,13 +138,13 @@
     private final LocalLog mFlushHistory = new LocalLog(10);
 
     /** @hide */
-    protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
-            @Nullable IContentCaptureManager systemServerInterface,
-            @NonNull boolean disabled) {
+    protected MainContentCaptureSession(@NonNull Context context,
+            @NonNull ContentCaptureManager manager, @NonNull Handler handler,
+            @Nullable IContentCaptureManager systemServerInterface) {
         mContext = context;
+        mManager = manager;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
-        mDisabled = new AtomicBoolean(disabled);
     }
 
     @Override
@@ -235,8 +237,8 @@
 
     /**
      * Callback from {@code system_server} after call to
-     * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
-     * int, IResultReceiver)}.
+     * {@link IContentCaptureManager#startSession(IBinder, ComponentName, String, int,
+     * IResultReceiver)}
      *
      * @param resultCode session state
      * @param binder handle to {@code IContentCaptureDirectManager}
@@ -517,8 +519,12 @@
 
     @Override
     boolean isContentCaptureEnabled() {
-        return super.isContentCaptureEnabled() && mSystemServerInterface != null
-                && !mDisabled.get();
+        return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+    }
+
+    // Called by ContentCaptureManager.isContentCaptureEnabled
+    boolean isDisabled() {
+        return mDisabled.get();
     }
 
     // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
new file mode 100644
index 0000000..602455c
--- /dev/null
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.Nullable;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class for inserting and retrieving data in TextClassifier request/response extras.
+ * @hide
+ */
+public final class ExtrasUtils {
+
+    private static final String ACTIONS_INTENTS = "actions-intents";
+    private static final String FOREIGN_LANGUAGE = "foreign-language";
+    private static final String ENTITY_TYPE = "entity-type";
+    private static final String SCORE = "score";
+    private static final String MODEL_VERSION = "model-version";
+    private static final String MODEL_NAME = "model-name";
+
+    private ExtrasUtils() {}
+
+    /**
+     * Bundles and returns foreign language detection information for TextClassifier responses.
+     */
+    static Bundle createForeignLanguageExtra(
+            String language, float score, int modelVersion) {
+        final Bundle bundle = new Bundle();
+        bundle.putString(ENTITY_TYPE, language);
+        bundle.putFloat(SCORE, score);
+        bundle.putInt(MODEL_VERSION, modelVersion);
+        bundle.putString(MODEL_NAME, "langId_v" + modelVersion);
+        return bundle;
+    }
+
+    /**
+     * Stores {@code extra} as foreign language information in TextClassifier response object's
+     * extras {@code container}.
+     */
+    static void putForeignLanguageExtra(Bundle container, Bundle extra) {
+        container.putParcelable(FOREIGN_LANGUAGE, extra);
+    }
+
+    /**
+     * Returns foreign language detection information contained in the TextClassification object.
+     * responses.
+     */
+    @Nullable
+    public static Bundle getForeignLanguageExtra(TextClassification classification) {
+        return classification.getExtras().getBundle(FOREIGN_LANGUAGE);
+    }
+
+    /**
+     * Stores {@code actionIntents} information in TextClassifier response object's extras
+     * {@code container}.
+     */
+    static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
+        container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
+    }
+
+    /**
+     * Returns {@code actionIntents} information contained in the TextClassification object.
+     */
+    @Nullable
+    public static ArrayList<Intent> getActionsIntents(TextClassification classification) {
+        return classification.getExtras().getParcelableArrayList(ACTIONS_INTENTS);
+    }
+
+    /**
+     * Returns the first "translate" action found in the {@code classification} object.
+     */
+    @Nullable
+    public static RemoteAction findTranslateAction(TextClassification classification) {
+        final ArrayList<Intent> actionIntents = getActionsIntents(classification);
+        if (actionIntents != null) {
+            final int size = actionIntents.size();
+            for (int i = 0; i < size; i++) {
+                if (Intent.ACTION_TRANSLATE.equals(actionIntents.get(i).getAction())) {
+                    return classification.getActions().get(i);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the entity type contained in the {@code extra}.
+     */
+    @Nullable
+    public static String getEntityType(Bundle extra) {
+        return extra.getString(ENTITY_TYPE);
+    }
+
+    /**
+     * Returns the score contained in the {@code extra}.
+     */
+    @Nullable
+    public static float getScore(Bundle extra) {
+        return extra.getFloat(SCORE, -1);
+    }
+
+    /**
+     * Returns the model name contained in the {@code extra}.
+     */
+    @Nullable
+    public static String getModelName(Bundle extra) {
+        return extra.getString(MODEL_NAME);
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index d9f7965..a059209 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -378,6 +378,8 @@
         @Nullable private OnClickListener mLegacyOnClickListener;
         @Nullable private String mId;
         @Nullable private Bundle mExtras;
+        @NonNull private final ArrayList<Intent> mActionIntents = new ArrayList<>();
+        @Nullable private Bundle mForeignLanguageExtra;
 
         /**
          * Sets the classified text.
@@ -412,8 +414,19 @@
          */
         @NonNull
         public Builder addAction(@NonNull RemoteAction action) {
+            return addAction(action, null);
+        }
+
+        /**
+         * @param intent the intent in the remote action.
+         * @see #addAction(RemoteAction)
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public Builder addAction(RemoteAction action, @Nullable Intent intent) {
             Preconditions.checkArgument(action != null);
             mActions.add(action);
+            mActionIntents.add(intent);
             return this;
         }
 
@@ -499,13 +512,33 @@
         }
 
         /**
+         * @see #setExtras(Bundle)
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public Builder setForeignLanguageExtra(@Nullable Bundle extra) {
+            mForeignLanguageExtra = extra;
+            return this;
+        }
+
+        /**
          * Builds and returns a {@link TextClassification} object.
          */
         @NonNull
         public TextClassification build() {
             return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
-                    mLegacyOnClickListener, mActions, mEntityConfidence, mId,
-                    mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+                    mLegacyOnClickListener, mActions, mEntityConfidence, mId, buildExtras());
+        }
+
+        private Bundle buildExtras() {
+            final Bundle extras = mExtras == null ? new Bundle() : mExtras.deepCopy();
+            if (!mActionIntents.isEmpty()) {
+                ExtrasUtils.putActionsIntents(extras, mActionIntents);
+            }
+            if (mForeignLanguageExtra != null) {
+                ExtrasUtils.putForeignLanguageExtra(extras, mForeignLanguageExtra);
+            }
+            return extras.isEmpty() ? Bundle.EMPTY : extras;
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 45668c0..ba1287f 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -71,10 +71,23 @@
 
     @Override
     public void onSelectionEvent(SelectionEvent event) {
-        checkDestroyed();
-        Preconditions.checkNotNull(event);
-        if (mEventHelper.sanitizeEvent(event)) {
-            mDelegate.onSelectionEvent(event);
+        try {
+            if (mEventHelper.sanitizeEvent(event)) {
+                mDelegate.onSelectionEvent(event);
+            }
+        } catch (Exception e) {
+            // Avoid crashing for event reporting.
+            Log.e(LOG_TAG, "Error reporting text classifier selection event", e);
+        }
+    }
+
+    @Override
+    public void onTextClassifierEvent(TextClassifierEvent event) {
+        try {
+            mDelegate.onTextClassifierEvent(event);
+        } catch (Exception e) {
+            // Avoid crashing for event reporting.
+            Log.e(LOG_TAG, "Error reporting text classifier event", e);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 8a688d8..e010155 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -161,7 +161,12 @@
      * No-op TextClassifier.
      * This may be used to turn off TextClassifier features.
      */
-    TextClassifier NO_OP = new TextClassifier() {};
+    TextClassifier NO_OP = new TextClassifier() {
+        @Override
+        public String toString() {
+            return "TextClassifier.NO_OP";
+        }
+    };
 
     /**
      * Extra that is included on activity intents coming from a TextClassifier when
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index cd13cc0..0d4338b 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -141,6 +141,8 @@
     @Nullable private final String mLanguage;
     private final float mScore;
 
+    @Nullable private final String mModelName;
+
     private TextClassifierEvent(
             int eventCategory,
             int eventType,
@@ -156,7 +158,8 @@
             int relativeSuggestedWordEndIndex,
             int[] actionIndex,
             String language,
-            float score) {
+            float score,
+            String modelVersion) {
         mEventCategory = eventCategory;
         mEventType = eventType;
         mEntityTypes = entityTypes;
@@ -172,6 +175,7 @@
         mActionIndices = actionIndex;
         mLanguage = language;
         mScore = score;
+        mModelName = modelVersion;
     }
 
     @Override
@@ -196,6 +200,7 @@
         dest.writeIntArray(mActionIndices);
         dest.writeString(mLanguage);
         dest.writeFloat(mScore);
+        dest.writeString(mModelName);
     }
 
     private static TextClassifierEvent readFromParcel(Parcel in) {
@@ -214,7 +219,8 @@
                 /* relativeSuggestedWordEndIndex= */ in.readInt(),
                 /* actionIndices= */ in.createIntArray(),
                 /* language= */ in.readString(),
-                /* score= */ in.readFloat());
+                /* score= */ in.readFloat(),
+                /* modelVersion= */ in.readString());
     }
 
     /**
@@ -264,6 +270,7 @@
         return mEventIndex;
     }
 
+    // TODO: Remove this API.
     /**
      * Returns the time this event occurred. This is the number of milliseconds since
      * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
@@ -339,6 +346,15 @@
     }
 
     /**
+     * Returns the model name.
+     * @hide
+     */
+    @Nullable
+    public String getModelName() {
+        return mModelName;
+    }
+
+    /**
      * Builder to build a text classifier event.
      */
     public static final class Builder {
@@ -359,6 +375,8 @@
         @Nullable private String mLanguage;
         private float mScore;
 
+        private String mModelName;
+
         /**
          * Creates a builder for building {@link TextClassifierEvent}s.
          *
@@ -407,6 +425,7 @@
             return this;
         }
 
+        // TODO: Remove this API.
         /**
          * Sets the time this event occurred. This is the number of milliseconds since
          * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
@@ -501,6 +520,15 @@
         }
 
         /**
+         * Sets the model name string.
+         * @hide
+         */
+        public Builder setModelName(@Nullable String modelVersion) {
+            mModelName = modelVersion;
+            return this;
+        }
+
+        /**
          * Builds and returns a text classifier event.
          */
         @NonNull
@@ -521,7 +549,8 @@
                     mRelativeSuggestedWordEndIndex,
                     mActionIndices,
                     mLanguage,
-                    mScore);
+                    mScore,
+                    mModelName);
         }
         // TODO: Add build(boolean validate).
     }
@@ -544,6 +573,7 @@
         out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
         out.append(", mLanguage=").append(mLanguage);
         out.append(", mScore=").append(mScore);
+        out.append(", mModelName=").append(mModelName);
         out.append("}");
         return out.toString();
     }
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
index 5563dfc..6a12250 100644
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -16,7 +16,6 @@
 package android.view.textclassifier;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
@@ -46,7 +45,7 @@
     private final MetricsLogger mMetricsLogger;
 
     public TextClassifierEventTronLogger() {
-        mMetricsLogger = new MetricsLogger();
+        this(new MetricsLogger());
     }
 
     @VisibleForTesting
@@ -57,6 +56,7 @@
     /** Emits a text classifier event to the logs. */
     public void writeEvent(TextClassifierEvent event) {
         Preconditions.checkNotNull(event);
+
         int category = getCategory(event);
         if (category == -1) {
             Log.w(TAG, "Unknown category: " + event.getEventCategory());
@@ -65,14 +65,12 @@
         final LogMaker log = new LogMaker(category)
                 .setSubtype(getLogType(event))
                 .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
-                .addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
-                .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
-                        SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
+                .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event))
                 .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore());
 
         String[] entityTypes = event.getEntityTypes();
-        // TRON does not support a field of list type, and thus workaround by store them
-        // in three separate fields. This is no longer an issue once we have moved to Westworld.
+        // The old logger does not support a field of list type, and thus workaround by store them
+        // in three separate fields. This is not an issue with the new logger.
         if (entityTypes.length >= 1) {
             log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
         }
@@ -93,6 +91,13 @@
         debugLog(log);
     }
 
+    private static String getModelName(TextClassifierEvent event) {
+        if (event.getModelName() != null) {
+            return event.getModelName();
+        }
+        return SelectionSessionLogger.SignatureParser.getModelName(event.getResultId());
+    }
+
     private static int getCategory(TextClassifierEvent event) {
         switch (event.getEventCategory()) {
             case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index c297928..14afa33 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -65,9 +65,6 @@
  */
 public final class TextClassifierImpl implements TextClassifier {
 
-    /** @hide */
-    public static final String ACTIONS_INTENTS = "actions-intents";
-
     private static final String LOG_TAG = DEFAULT_LOG_TAG;
 
     private static final boolean DEBUG = false;
@@ -343,7 +340,11 @@
         if (DEBUG) {
             Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
         }
-        mTextClassifierEventTronLogger.writeEvent(event);
+        try {
+            mTextClassifierEventTronLogger.writeEvent(event);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error writing event", e);
+        }
     }
 
     /** @inheritDoc */
@@ -566,12 +567,16 @@
         final float foreignTextThreshold = mSettings.getLangIdThresholdOverride() >= 0
                 ? mSettings.getLangIdThresholdOverride()
                 : 0.5f /* TODO: Load this from the langId model. */;
+        final Bundle foreignLanguageBundle =
+                detectForeignLanguage(classifiedText, foreignTextThreshold);
+        builder.setForeignLanguageExtra(foreignLanguageBundle);
+
         boolean isPrimaryAction = true;
         final ArrayList<Intent> sourceIntents = new ArrayList<>();
         List<LabeledIntent> labeledIntents = mIntentFactory.create(
                 mContext,
                 classifiedText,
-                isForeignText(classifiedText, foreignTextThreshold),
+                foreignLanguageBundle != null,
                 referenceTime,
                 highestScoringResult);
         for (LabeledIntent labeledIntent : labeledIntents) {
@@ -590,28 +595,28 @@
                                 labeledIntent.getIntent(), labeledIntent.getRequestCode())));
                 isPrimaryAction = false;
             }
-            builder.addAction(action);
-            sourceIntents.add(labeledIntent.getIntent());
+            builder.addAction(action, labeledIntent.getIntent());
         }
 
-        final Bundle extras = new Bundle();
-        extras.putParcelableArrayList(ACTIONS_INTENTS, sourceIntents);
-
-        return builder.setId(createId(text, start, end))
-                .setExtras(extras)
-                .build();
+        return builder.setId(createId(text, start, end)).build();
     }
 
-    private boolean isForeignText(String text, float threshold) {
+    /**
+     * Returns a bundle with the language and confidence score if it finds the text to be
+     * in a foreign language. Otherwise returns null.
+     */
+    @Nullable
+    private Bundle detectForeignLanguage(String text, float threshold) {
         if (threshold > 1) {
-            return false;
+            return null;
         }
 
         // TODO: Revisit this algorithm.
         try {
-            final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+            final LangIdModel langId = getLangIdImpl();
+            final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text);
             if (langResults.length <= 0) {
-                return false;
+                return null;
             }
 
             LangIdModel.LanguageResult highestScoringResult = langResults[0];
@@ -621,7 +626,7 @@
                 }
             }
             if (highestScoringResult.getScore() < threshold) {
-                return false;
+                return null;
             }
             // TODO: Remove
             Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
@@ -632,14 +637,15 @@
             final int size = deviceLocales.size();
             for (int i = 0; i < size; i++) {
                 if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
-                    return false;
+                    return null;
                 }
             }
-            return true;
+            return ExtrasUtils.createForeignLanguageExtra(
+                    detected.getLanguage(), highestScoringResult.getScore(), langId.getVersion());
         } catch (Throwable t) {
             Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
         }
-        return false;
+        return null;
     }
 
     @Override
@@ -773,3 +779,4 @@
         }
     }
 }
+
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4a60b6a..e9d72a2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4112,9 +4112,9 @@
             }
         }
 
-        private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int intemId, int order,
+        private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int itemId, int order,
                 int showAsAction) {
-            final MenuItem item = menu.add(TextView.ID_ASSIST, intemId, order, action.getTitle())
+            final MenuItem item = menu.add(TextView.ID_ASSIST, itemId, order, action.getTitle())
                     .setContentDescription(action.getContentDescription());
             if (action.shouldShowIcon()) {
                 item.setIcon(action.getIcon().loadDrawable(mTextView.getContext()));
@@ -4188,7 +4188,8 @@
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            getSelectionActionModeHelper().onSelectionAction(item.getItemId());
+            getSelectionActionModeHelper()
+                    .onSelectionAction(item.getItemId(), item.getTitle().toString());
 
             if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
                 return true;
@@ -4488,6 +4489,13 @@
         // when selecting text when the handles jump to the end / start of words which may be on
         // a different line.
         protected int mPreviousLineTouched = UNSET_LINE;
+        // The raw x coordinate of the motion down event which started the current dragging session.
+        // Only used and stored when magnifier is used.
+        private float mCurrentDragInitialTouchRawX = UNSET_X_VALUE;
+        // The scale transform applied by containers to the TextView. Only used and computed
+        // when magnifier is used.
+        private float mTextViewScaleX;
+        private float mTextViewScaleY;
 
         private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) {
             super(mTextView.getContext());
@@ -4807,25 +4815,44 @@
                             / mMagnifierAnimator.mMagnifier.getZoom());
             final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
             final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
-            return glyphHeight > magnifierContentHeight;
+            return glyphHeight * mTextViewScaleY > magnifierContentHeight;
         }
 
-        private boolean viewIsMatrixTransformed() {
+        /**
+         * Traverses the hierarchy above the text view, and computes the total scale applied
+         * to it. If a rotation is encountered, the method returns {@code false}, indicating
+         * that the magnifier should not be shown anyways. It would be nice to keep these two
+         * pieces of logic separate (the rotation check and the total scale calculation),
+         * but for efficiency we can do them in a single go.
+         * @return whether the text view is rotated
+         */
+        private boolean checkForTransforms() {
             if (mMagnifierAnimator.mMagnifierIsShowing) {
                 // Do not check again when the magnifier is currently showing.
-                return false;
-            }
-            if (!mTextView.hasIdentityMatrix()) {
                 return true;
             }
+
+            if (mTextView.getRotation() != 0f || mTextView.getRotationX() != 0f
+                    || mTextView.getRotationY() != 0f) {
+                return false;
+            }
+            mTextViewScaleX = mTextView.getScaleX();
+            mTextViewScaleY = mTextView.getScaleY();
+
             ViewParent viewParent = mTextView.getParent();
             while (viewParent != null) {
-                if (viewParent instanceof View && !((View) viewParent).hasIdentityMatrix()) {
-                    return true;
+                if (viewParent instanceof View) {
+                    final View view = (View) viewParent;
+                    if (view.getRotation() != 0f || view.getRotationX() != 0f
+                            || view.getRotationY() != 0f) {
+                        return false;
+                    }
+                    mTextViewScaleX *= view.getScaleX();
+                    mTextViewScaleY *= view.getScaleY();
                 }
                 viewParent = viewParent.getParent();
             }
-            return false;
+            return true;
         }
 
         /**
@@ -4863,6 +4890,12 @@
                 return false;
             }
 
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mCurrentDragInitialTouchRawX = event.getRawX();
+            } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+                mCurrentDragInitialTouchRawX = UNSET_X_VALUE;
+            }
+
             final Layout layout = mTextView.getLayout();
             final int lineNumber = layout.getLineForOffset(offset);
             // Compute whether the selection handles are currently on the same line, and,
@@ -4890,6 +4923,8 @@
             } else {
                 rightBound += mTextView.getLayout().getLineRight(lineNumber);
             }
+            leftBound *= mTextViewScaleX;
+            rightBound *= mTextViewScaleX;
             final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth()
                     / mMagnifierAnimator.mMagnifier.getZoom());
             if (touchXInView < leftBound - contentWidth / 2
@@ -4897,13 +4932,27 @@
                 // The touch is too far from the current line / selection, so hide the magnifier.
                 return false;
             }
-            showPosInView.x = Math.max(leftBound, Math.min(rightBound, touchXInView));
+
+            final float scaledTouchXInView;
+            if (mTextViewScaleX == 1f) {
+                // In the common case, do not use mCurrentDragInitialTouchRawX to compute this
+                // coordinate, although the formula on the else branch should be equivalent.
+                // Since the formula relies on mCurrentDragInitialTouchRawX being set on
+                // MotionEvent.ACTION_DOWN, this makes us more defensive against cases when
+                // the sequence of events might not look as expected: for example, a sequence of
+                // ACTION_MOVE not preceded by ACTION_DOWN.
+                scaledTouchXInView = touchXInView;
+            } else {
+                scaledTouchXInView = (event.getRawX() - mCurrentDragInitialTouchRawX)
+                        * mTextViewScaleX + mCurrentDragInitialTouchRawX
+                        - textViewLocationOnScreen[0];
+            }
+            showPosInView.x = Math.max(leftBound, Math.min(rightBound, scaledTouchXInView));
 
             // Vertically snap to middle of current line.
-            showPosInView.y = (mTextView.getLayout().getLineTop(lineNumber)
+            showPosInView.y = ((mTextView.getLayout().getLineTop(lineNumber)
                     + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
-                    + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
-
+                    + mTextView.getTotalPaddingTop() - mTextView.getScrollY()) * mTextViewScaleY;
             return true;
         }
 
@@ -4955,8 +5004,8 @@
             }
 
             final PointF showPosInView = new PointF();
-            final boolean shouldShow = !tooLargeTextForMagnifier()
-                    && !viewIsMatrixTransformed()
+            final boolean shouldShow = checkForTransforms() /*check not rotated and compute scale*/
+                    && !tooLargeTextForMagnifier()
                     && obtainMagnifierShowCoordinates(event, showPosInView);
             if (shouldShow) {
                 // Make the cursor visible and stop blinking.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 5147306..d5b1a3d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -579,24 +579,11 @@
             zoomCenterY = Math.round(yPosInView + mViewCoordinatesInSurface[1]);
         }
 
-        final Rect[] bounds = new Rect[3]; // [MAX_IN_SURFACE, MAX_IN_VIEW, MAX_VISIBLE]
+        final Rect[] bounds = new Rect[2]; // [MAX_IN_SURFACE, MAX_VISIBLE]
         // Obtain the surface bounds rectangle.
         final Rect surfaceBounds = new Rect(0, 0,
                 mContentCopySurface.mWidth, mContentCopySurface.mHeight);
         bounds[0] = surfaceBounds;
-        // Obtain the view bounds rectangle.
-        final Rect viewBounds;
-        if (mView instanceof SurfaceView) {
-            viewBounds = new Rect(0, 0, mContentCopySurface.mWidth, mContentCopySurface.mHeight);
-        } else {
-            viewBounds = new Rect(
-                    mViewCoordinatesInSurface[0],
-                    mViewCoordinatesInSurface[1],
-                    mViewCoordinatesInSurface[0] + mView.getWidth(),
-                    mViewCoordinatesInSurface[1] + mView.getHeight()
-            );
-        }
-        bounds[1] = viewBounds;
         // Obtain the visible view region rectangle.
         final Rect viewVisibleRegion = new Rect();
         mView.getGlobalVisibleRect(viewVisibleRegion);
@@ -609,7 +596,7 @@
             // If we copy content from a SurfaceView, clamp coordinates relative to it.
             viewVisibleRegion.offset(-mViewCoordinatesInSurface[0], -mViewCoordinatesInSurface[1]);
         }
-        bounds[2] = viewVisibleRegion;
+        bounds[1] = viewVisibleRegion;
 
         // Aggregate the above to obtain the bounds where the content copy will be restricted.
         int resolvedLeft = Integer.MIN_VALUE;
@@ -1301,11 +1288,6 @@
          *   {@link android.view.View#getGlobalVisibleRect(Rect)}. For example, this will take into
          *   account the case when the view is contained in a scrollable container, and the
          *   magnifier will refuse to copy content outside of the visible view region</li>
-         *   <li>{@link #SOURCE_BOUND_MAX_IN_VIEW}, which extends the bound as much as possible
-         *   while remaining in the bounds of the view. Note that, although this option is
-         *   used, the magnifier will always only display content visible on the screen: if the
-         *   view lays outside the screen or is covered by a different view either partially or
-         *   totally, the magnifier will not show any view region not visible on the screen.</li>
          *   <li>{@link #SOURCE_BOUND_MAX_IN_SURFACE}, which extends the bound as much
          *   as possible while remaining inside the surface the content is copied from.</li>
          * </ul>
@@ -1349,21 +1331,14 @@
      * A source bound that will extend as much as possible, while remaining within the surface
      * the content is copied from.
      */
-
     public static final int SOURCE_BOUND_MAX_IN_SURFACE = 0;
-    /**
-     * A source bound that will extend as much as possible, while remaining within the
-     * magnified view.
-     */
-
-    public static final int SOURCE_BOUND_MAX_IN_VIEW = 1;
 
     /**
      * A source bound that will extend as much as possible, while remaining within the
      * visible region of the magnified view, as determined by
      * {@link View#getGlobalVisibleRect(Rect)}.
      */
-    public static final int SOURCE_BOUND_MAX_VISIBLE = 2;
+    public static final int SOURCE_BOUND_MAX_VISIBLE = 1;
 
 
     /**
@@ -1373,7 +1348,7 @@
      *
      * @hide
      */
-    @IntDef({SOURCE_BOUND_MAX_IN_SURFACE, SOURCE_BOUND_MAX_IN_VIEW, SOURCE_BOUND_MAX_VISIBLE})
+    @IntDef({SOURCE_BOUND_MAX_IN_SURFACE, SOURCE_BOUND_MAX_VISIBLE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SourceBound {}
 
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 4caf288..564cfdd 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -20,12 +20,14 @@
 import android.annotation.Nullable;
 import android.annotation.UiThread;
 import android.annotation.WorkerThread;
+import android.app.RemoteAction;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.text.Layout;
 import android.text.Selection;
@@ -34,13 +36,16 @@
 import android.text.util.Linkify;
 import android.util.Log;
 import android.view.ActionMode;
+import android.view.textclassifier.ExtrasUtils;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 import android.view.textclassifier.SelectionSessionLogger;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
 import android.view.textclassifier.TextSelection;
 import android.widget.Editor.SelectionModifierCursorController;
 
@@ -166,16 +171,17 @@
         }
     }
 
-    public void onSelectionAction(int menuItemId) {
+    /** Reports a selection action event. */
+    public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
         mSelectionTracker.onSelectionAction(
                 mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
-                getActionType(menuItemId), mTextClassification);
+                getActionType(menuItemId), actionLabel, mTextClassification);
     }
 
     public void onSelectionDrag() {
         mSelectionTracker.onSelectionAction(
                 mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
-                SelectionEvent.ACTION_DRAG, mTextClassification);
+                SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
     }
 
     public void onTextChanged(int start, int end) {
@@ -511,8 +517,11 @@
             mOriginalEnd = mSelectionEnd = selectionEnd;
             mAllowReset = false;
             maybeInvalidateLogger();
-            mLogger.logSelectionStarted(mTextView.getTextClassificationSession(),
-                    text, selectionStart,
+            mLogger.logSelectionStarted(
+                    mTextView.getTextClassificationSession(),
+                    mTextView.getTextClassificationContext(),
+                    text,
+                    selectionStart,
                     isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL);
         }
 
@@ -570,10 +579,12 @@
         public void onSelectionAction(
                 int selectionStart, int selectionEnd,
                 @SelectionEvent.ActionType int action,
+                @Nullable String actionLabel,
                 @Nullable TextClassification classification) {
             if (isSelectionStarted()) {
                 mAllowReset = false;
-                mLogger.logSelectionAction(selectionStart, selectionEnd, action, classification);
+                mLogger.logSelectionAction(
+                        selectionStart, selectionEnd, action, actionLabel, classification);
             }
         }
 
@@ -596,7 +607,8 @@
                     mSelectionEnd = editor.getTextView().getSelectionEnd();
                     mLogger.logSelectionAction(
                             textView.getSelectionStart(), textView.getSelectionEnd(),
-                            SelectionEvent.ACTION_RESET, null /* classification */);
+                            SelectionEvent.ACTION_RESET,
+                            /* actionLabel= */ null, /* classification= */ null);
                 }
                 return selected;
             }
@@ -605,7 +617,9 @@
 
         public void onTextChanged(int start, int end, TextClassification classification) {
             if (isSelectionStarted() && start == mSelectionStart && end == mSelectionEnd) {
-                onSelectionAction(start, end, SelectionEvent.ACTION_OVERTYPE, classification);
+                onSelectionAction(
+                        start, end, SelectionEvent.ACTION_OVERTYPE,
+                        /* actionLabel= */ null, classification);
             }
         }
 
@@ -644,7 +658,8 @@
                 if (mIsPending) {
                     mLogger.logSelectionAction(
                             mSelectionStart, mSelectionEnd,
-                            SelectionEvent.ACTION_ABANDON, null /* classification */);
+                            SelectionEvent.ACTION_ABANDON,
+                            /* actionLabel= */ null, /* classification= */ null);
                     mSelectionStart = mSelectionEnd = -1;
                     mLogger.endTextClassificationSession();
                     mIsPending = false;
@@ -679,6 +694,11 @@
         private final BreakIterator mTokenIterator;
 
         @Nullable private TextClassifier mClassificationSession;
+        @Nullable private TextClassificationContext mClassificationContext;
+
+        @Nullable private TextClassifierEvent mTranslateViewEvent;
+        @Nullable private TextClassifierEvent mTranslateClickEvent;
+
         private int mStartIndex;
         private String mText;
 
@@ -690,6 +710,7 @@
 
         public void logSelectionStarted(
                 TextClassifier classificationSession,
+                TextClassificationContext classificationContext,
                 CharSequence text, int index,
                 @InvocationMethod int invocationMethod) {
             try {
@@ -701,6 +722,7 @@
                 mTokenIterator.setText(mText);
                 mStartIndex = index;
                 mClassificationSession = classificationSession;
+                mClassificationContext = classificationContext;
                 if (hasActiveClassificationSession()) {
                     mClassificationSession.onSelectionEvent(
                             SelectionEvent.createSelectionStartedEvent(invocationMethod, 0));
@@ -731,6 +753,7 @@
                                 SelectionEvent.createSelectionModifiedEvent(
                                         wordIndices[0], wordIndices[1]));
                     }
+                    maybeGenerateTranslateViewEvent(classification);
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
@@ -741,6 +764,7 @@
         public void logSelectionAction(
                 int start, int end,
                 @SelectionEvent.ActionType int action,
+                @Nullable String actionLabel,
                 @Nullable TextClassification classification) {
             try {
                 if (hasActiveClassificationSession()) {
@@ -757,6 +781,9 @@
                                 SelectionEvent.createSelectionActionEvent(
                                         wordIndices[0], wordIndices[1], action));
                     }
+
+                    maybeGenerateTranslateClickEvent(classification, actionLabel);
+
                     if (SelectionEvent.isTerminal(action)) {
                         endTextClassificationSession();
                     }
@@ -773,6 +800,7 @@
 
         public void endTextClassificationSession() {
             if (hasActiveClassificationSession()) {
+                maybeReportTranslateEvents();
                 mClassificationSession.destroy();
             }
         }
@@ -843,6 +871,78 @@
         private boolean isWhitespace(int start, int end) {
             return PATTERN_WHITESPACE.matcher(mText.substring(start, end)).matches();
         }
+
+        private void maybeGenerateTranslateViewEvent(@Nullable TextClassification classification) {
+            if (classification != null) {
+                final TextClassifierEvent event = generateTranslateEvent(
+                        TextClassifierEvent.TYPE_ACTIONS_SHOWN,
+                        classification, mClassificationContext, /* actionLabel= */null);
+                mTranslateViewEvent = (event != null) ? event : mTranslateViewEvent;
+            }
+        }
+
+        private void maybeGenerateTranslateClickEvent(
+                @Nullable TextClassification classification, String actionLabel) {
+            if (classification != null) {
+                mTranslateClickEvent = generateTranslateEvent(
+                        TextClassifierEvent.TYPE_SMART_ACTION,
+                        classification, mClassificationContext, actionLabel);
+            }
+        }
+
+        private void maybeReportTranslateEvents() {
+            // Translate view and click events should only be logged once per selection session.
+            if (mTranslateViewEvent != null) {
+                mClassificationSession.onTextClassifierEvent(mTranslateViewEvent);
+                mTranslateViewEvent = null;
+            }
+            if (mTranslateClickEvent != null) {
+                mClassificationSession.onTextClassifierEvent(mTranslateClickEvent);
+                mTranslateClickEvent = null;
+            }
+        }
+
+        @Nullable
+        private static TextClassifierEvent generateTranslateEvent(
+                int eventType, TextClassification classification,
+                TextClassificationContext classificationContext, @Nullable String actionLabel) {
+
+            // The platform attempts to log "views" and "clicks" of the "Translate" action.
+            // Views are logged if a user is presented with the translate action during a selection
+            // session.
+            // Clicks are logged if the user clicks on the translate action.
+            // The index of the translate action is also logged to indicate whether it might have
+            // been in the main panel or overflow panel of the selection toolbar.
+            // NOTE that the "views" metric may be flawed if a TextView removes the translate menu
+            // item via a custom action mode callback or does not show a selection menu item.
+
+            final RemoteAction translateAction = ExtrasUtils.findTranslateAction(classification);
+            if (translateAction == null) {
+                // No translate action present. Nothing to log. Exit.
+                return null;
+            }
+
+            if (eventType == TextClassifierEvent.TYPE_SMART_ACTION
+                    && !translateAction.getTitle().toString().equals(actionLabel)) {
+                // Clicked action is not a translate action. Nothing to log. Exit.
+                // Note that we don't expect an actionLabel for "view" events.
+                return null;
+            }
+
+            final Bundle foreignLanguageExtra = ExtrasUtils.getForeignLanguageExtra(classification);
+            final String language = ExtrasUtils.getEntityType(foreignLanguageExtra);
+            final float score = ExtrasUtils.getScore(foreignLanguageExtra);
+            final String model = ExtrasUtils.getModelName(foreignLanguageExtra);
+            return new TextClassifierEvent.Builder(
+                    TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType)
+                    .setEventContext(classificationContext)
+                    .setResultId(classification.getId())
+                    .setEntityTypes(language)
+                    .setScore(score)
+                    .setActionIndices(classification.getActions().indexOf(translateAction))
+                    .setModelName(model)
+                    .build();
+        }
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2b45c06..8029cf0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -456,6 +456,7 @@
 
     private TextClassifier mTextClassifier;
     private TextClassifier mTextClassificationSession;
+    private TextClassificationContext mTextClassificationContext;
 
     // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
     // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
@@ -12068,16 +12069,15 @@
                 } else {
                     widgetType = TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
                 }
-                final TextClassificationContext textClassificationContext =
-                        new TextClassificationContext.Builder(
-                                mContext.getPackageName(), widgetType)
-                                .build();
+                mTextClassificationContext = new TextClassificationContext.Builder(
+                        mContext.getPackageName(), widgetType)
+                        .build();
                 if (mTextClassifier != null) {
                     mTextClassificationSession = tcm.createTextClassificationSession(
-                            textClassificationContext, mTextClassifier);
+                            mTextClassificationContext, mTextClassifier);
                 } else {
                     mTextClassificationSession = tcm.createTextClassificationSession(
-                            textClassificationContext);
+                            mTextClassificationContext);
                 }
             } else {
                 mTextClassificationSession = TextClassifier.NO_OP;
@@ -12087,6 +12087,15 @@
     }
 
     /**
+     * Returns the {@link TextClassificationContext} for the current TextClassifier session.
+     * @see #getTextClassificationSession()
+     */
+    @Nullable
+    TextClassificationContext getTextClassificationContext() {
+        return mTextClassificationContext;
+    }
+
+    /**
      * Returns true if this TextView uses a no-op TextClassifier.
      */
     boolean usesNoOpTextClassifier() {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 46f42f7..cfe2939 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -25,6 +25,7 @@
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.AppTargetId;
 import android.content.ClipData;
+import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -80,11 +81,13 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.AbsListView;
 import android.widget.BaseAdapter;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.Space;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -350,6 +353,50 @@
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
 
+        Button copyButton = findViewById(R.id.copy_button);
+        copyButton.setOnClickListener(view -> {
+            Intent targetIntent = getTargetIntent();
+            if (targetIntent == null) {
+                finish();
+            } else {
+                final String action = targetIntent.getAction();
+
+                ClipData clipData = null;
+                if (Intent.ACTION_SEND.equals(action)) {
+                    String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
+                    Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+
+                    if (extraText != null) {
+                        clipData = ClipData.newPlainText(null, extraText);
+                    } else if (extraStream != null) {
+                        clipData = ClipData.newUri(getContentResolver(), null, extraStream);
+                    } else {
+                        Log.w(TAG, "No data available to copy to clipboard");
+                        return;
+                    }
+                } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+                    final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
+                            Intent.EXTRA_STREAM);
+                    clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
+                    for (int i = 1; i < streams.size(); i++) {
+                        clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
+                    }
+                } else {
+                    // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
+                    // so warn about unexpected action
+                    Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
+                    return;
+                }
+
+                ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
+                        Context.CLIPBOARD_SERVICE);
+                clipboardManager.setPrimaryClip(clipData);
+                Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
+
+                finish();
+            }
+        });
+
         MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
 
         mChooserShownTime = System.currentTimeMillis();
@@ -414,39 +461,39 @@
     private void showDefaultContentPreview(final ViewGroup parentLayout,
             final Intent targetIntent) {
         CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
-        TextView previewTextView = findViewById(R.id.content_preview_text);
         if (sharingText == null) {
-            previewTextView.setVisibility(View.GONE);
+            findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE);
         } else {
-            previewTextView.setText(sharingText);
+            TextView textView = findViewById(R.id.content_preview_text);
+            textView.setText(sharingText);
         }
 
         String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
-        TextView previewTitleView = findViewById(R.id.content_preview_title);
-        if (previewTitle == null) {
-            previewTitleView.setVisibility(View.GONE);
+        if (previewTitle == null || previewTitle.trim().isEmpty()) {
+            findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE);
         } else {
+            TextView previewTitleView = findViewById(R.id.content_preview_title);
             previewTitleView.setText(previewTitle);
-        }
 
-        ClipData previewData = targetIntent.getClipData();
-        Uri previewThumbnail = null;
-        if (previewData != null) {
-            if (previewData.getItemCount() > 0) {
-                ClipData.Item previewDataItem = previewData.getItemAt(0);
-                previewThumbnail = previewDataItem.getUri();
+            ClipData previewData = targetIntent.getClipData();
+            Uri previewThumbnail = null;
+            if (previewData != null) {
+                if (previewData.getItemCount() > 0) {
+                    ClipData.Item previewDataItem = previewData.getItemAt(0);
+                    previewThumbnail = previewDataItem.getUri();
+                }
             }
-        }
 
-        ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
-        if (previewThumbnail == null) {
-            previewThumbnailView.setVisibility(View.GONE);
-        } else {
-            Bitmap bmp = loadThumbnail(previewThumbnail, new Size(200, 200));
-            if (bmp == null) {
+            ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
+            if (previewThumbnail == null) {
                 previewThumbnailView.setVisibility(View.GONE);
             } else {
-                previewThumbnailView.setImageBitmap(bmp);
+                Bitmap bmp = loadThumbnail(previewThumbnail, new Size(100, 100));
+                if (bmp == null) {
+                    previewThumbnailView.setVisibility(View.GONE);
+                } else {
+                    previewThumbnailView.setImageBitmap(bmp);
+                }
             }
         }
     }
@@ -2020,8 +2067,8 @@
         private void updatePath(int width, int height) {
             mPath.reset();
 
-            int imageWidth = width - getPaddingLeft() - getPaddingRight();
-            int imageHeight = height - getPaddingTop() - getPaddingBottom();
+            int imageWidth = width - getPaddingRight();
+            int imageHeight = height - getPaddingBottom();
             mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
                     mRadius, Path.Direction.CW);
         }
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 7a00a51..e19a32e 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -38,7 +38,7 @@
         return pulseOnNotificationEnabled(user)
                 || pulseOnLongPressEnabled(user)
                 || alwaysOnEnabled(user)
-                || wakeLockScreenGestureEnabled(user);
+                || wakeScreenGestureEnabled(user);
     }
 
     public boolean pulseOnNotificationEnabled(int user) {
@@ -76,11 +76,6 @@
         return !TextUtils.isEmpty(doubleTapSensorType());
     }
 
-    public boolean wakeLockScreenGestureEnabled(int user) {
-        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
-                && wakeScreenGestureAvailable();
-    }
-
     public boolean wakeScreenGestureAvailable() {
         return mContext.getResources()
                 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index da8605e..65b974b 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -104,6 +104,7 @@
     public boolean allowBypass;
     public boolean allowIPv4;
     public boolean allowIPv6;
+    public boolean isMetered = true;
     public Network[] underlyingNetworks;
     public ProxyInfo proxyInfo;
 
@@ -165,6 +166,7 @@
         out.writeInt(allowBypass ? 1 : 0);
         out.writeInt(allowIPv4 ? 1 : 0);
         out.writeInt(allowIPv6 ? 1 : 0);
+        out.writeInt(isMetered ? 1 : 0);
         out.writeTypedArray(underlyingNetworks, flags);
         out.writeParcelable(proxyInfo, flags);
     }
@@ -191,6 +193,7 @@
             config.allowBypass = in.readInt() != 0;
             config.allowIPv4 = in.readInt() != 0;
             config.allowIPv6 = in.readInt() != 0;
+            config.isMetered = in.readInt() != 0;
             config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
             config.proxyInfo = in.readParcelable(null);
             return config;
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
index 6ba584d..0e83e41 100644
--- a/core/java/com/android/internal/os/AppZygoteInit.java
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -17,13 +17,15 @@
 package com.android.internal.os;
 
 import android.app.LoadedApk;
+import android.app.ZygotePreload;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
 import android.util.Log;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 
 /**
@@ -76,20 +78,29 @@
 
             Zygote.allowAppFilesAcrossFork(appInfo);
 
-            Class<?> cl;
-            Method m;
-            try {
-                cl = Class.forName(appInfo.packageName + ".ZygotePreload", true, loader);
-                m = cl.getMethod("doPreload");
-                m.setAccessible(true);
-                m.invoke(null);
-            } catch (ClassNotFoundException e) {
-                // Don't treat this as an error since an app may not want to do any preloads
-                Log.w(TAG, "No ZygotePreload class found for " + appInfo.packageName);
-            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-                Log.e(TAG, "AppZygote application preload failed for "
-                        + appInfo.packageName, e);
+            if (appInfo.zygotePreloadName != null) {
+                Class<?> cl;
+                Method m;
+                try {
+                    ComponentName preloadName = ComponentName.createRelative(appInfo.packageName,
+                            appInfo.zygotePreloadName);
+                    cl = Class.forName(preloadName.getClassName(), true, loader);
+                    if (!ZygotePreload.class.isAssignableFrom(cl)) {
+                        Log.e(TAG, preloadName.getClassName() + " does not implement "
+                                + ZygotePreload.class.getName());
+                    } else {
+                        Constructor<?> ctor = cl.getConstructor();
+                        ZygotePreload preloadObject = (ZygotePreload) ctor.newInstance();
+                        preloadObject.doPreload(appInfo);
+                    }
+                } catch (ReflectiveOperationException e) {
+                    Log.e(TAG, "AppZygote application preload failed for "
+                            + appInfo.zygotePreloadName, e);
+                }
+            } else {
+                Log.i(TAG, "No zygotePreloadName attribute specified.");
             }
+
             try {
                 DataOutputStream socketOut = getSocketOutputStream();
                 socketOut.writeInt(loader != null ? 1 : 0);
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 2742b7c..b1328e8 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -37,9 +37,10 @@
  * frequency band.
  *
  * <p>Frequencies are bucketed together to reduce the amount of data created. This means that we
- * return {@link #NUM_BUCKETS} frequencies instead of the full number. Frequencies are reported as
- * the lowest frequency in that range. Frequencies are spread as evenly as possible across the
- * buckets. The buckets do not cross over the little/big frequencies reported.
+ * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of
+ * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest
+ * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The
+ * buckets do not cross over the little/big frequencies reported.
  *
  * <p>N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code
  * time_in_state} file contains every little core frequency in ascending order, followed by every
@@ -97,22 +98,17 @@
             DEFAULT_PROC_PATH.resolve("self/time_in_state");
 
     /**
-     * Number of frequency buckets
-     */
-    private static final int NUM_BUCKETS = 8;
-
-    /**
-     * Default predicate for what UIDs to check for when getting processes. This filters to only
-     * select UID 1000 (the {@code system} user)
-     */
-    private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
-
-    /**
      * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
      */
     private static final int ID_ERROR = -1;
 
     /**
+     * When checking whether to report data for a thread, we check the UID of the thread's owner
+     * against this predicate
+     */
+    private Predicate<Integer> mUidPredicate;
+
+    /**
      * Where the proc filesystem is mounted
      */
     private final Path mProcPath;
@@ -121,7 +117,7 @@
      * Frequencies read from the {@code time_in_state} file. Read from {@link
      * #mProcTimeInStateReader#getCpuFrequenciesKhz()} and cast to {@code int[]}
      */
-    private final int[] mFrequenciesKhz;
+    private int[] mFrequenciesKhz;
 
     /**
      * Used to read and parse {@code time_in_state} files
@@ -131,17 +127,10 @@
     /**
      * Used to sort frequencies and usage times into buckets
      */
-    private final FrequencyBucketCreator mFrequencyBucketCreator;
+    private FrequencyBucketCreator mFrequencyBucketCreator;
 
     private final Injector mInjector;
 
-    private KernelCpuThreadReader() throws IOException {
-        this(
-                DEFAULT_PROC_PATH,
-                DEFAULT_INITIAL_TIME_IN_STATE_PATH,
-                new Injector());
-    }
-
     /**
      * Create with a path where `proc` is mounted. Used primarily for testing
      *
@@ -151,17 +140,16 @@
      */
     @VisibleForTesting
     public KernelCpuThreadReader(
+            int numBuckets,
+            Predicate<Integer> uidPredicate,
             Path procPath,
             Path initialTimeInStatePath,
             Injector injector) throws IOException {
+        mUidPredicate = uidPredicate;
         mProcPath = procPath;
         mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
         mInjector = injector;
-
-        // Copy mProcTimeInState's frequencies and initialize bucketing
-        final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
-        mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, NUM_BUCKETS);
-        mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies(frequenciesKhz);
+        setNumBuckets(numBuckets);
     }
 
     /**
@@ -170,9 +158,14 @@
      * @return the reader, null if an exception was thrown during creation
      */
     @Nullable
-    public static KernelCpuThreadReader create() {
+    public static KernelCpuThreadReader create(int numBuckets, Predicate<Integer> uidPredicate) {
         try {
-            return new KernelCpuThreadReader();
+            return new KernelCpuThreadReader(
+                    numBuckets,
+                    uidPredicate,
+                    DEFAULT_PROC_PATH,
+                    DEFAULT_INITIAL_TIME_IN_STATE_PATH,
+                    new Injector());
         } catch (IOException e) {
             Slog.e(TAG, "Failed to initialize KernelCpuThreadReader", e);
             return null;
@@ -180,14 +173,6 @@
     }
 
     /**
-     * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)}
-     */
-    @Nullable
-    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
-        return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE);
-    }
-
-    /**
      * Get the per-thread CPU usage of all processes belonging to a set of UIDs
      *
      * <p>This function will crawl through all process {@code proc} directories found by the pattern
@@ -195,10 +180,11 @@
      * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive,
      * and should not be called more than once an hour.
      *
-     * @param uidPredicate only get usage from processes owned by UIDs that match this predicate
+     * <p>Data is only collected for UIDs passing the predicate supplied in {@link
+     * #setUidPredicate}.
      */
     @Nullable
-    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) {
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
         if (DEBUG) {
             Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
         }
@@ -213,7 +199,7 @@
                 if (uid == ID_ERROR || processId == ID_ERROR) {
                     continue;
                 }
-                if (!uidPredicate.test(uid)) {
+                if (!mUidPredicate.test(uid)) {
                     continue;
                 }
 
@@ -247,10 +233,7 @@
      */
     @Nullable
     public ProcessCpuUsage getCurrentProcessCpuUsage() {
-        return getProcessCpuUsage(
-                mProcPath.resolve("self"),
-                mInjector.myPid(),
-                mInjector.myUid());
+        return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid());
     }
 
     /**
@@ -300,6 +283,31 @@
     }
 
     /**
+     * Set the number of frequency buckets to use
+     */
+    void setNumBuckets(int numBuckets) {
+        if (numBuckets < 1) {
+            Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
+            return;
+        }
+        // If `numBuckets` hasn't changed since the last set, do nothing
+        if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
+            return;
+        }
+        mFrequencyBucketCreator = new FrequencyBucketCreator(
+                mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
+        mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies(
+                mProcTimeInStateReader.getFrequenciesKhz());
+    }
+
+    /**
+     * Set the UID predicate for {@link #getProcessCpuUsageByUids}
+     */
+    void setUidPredicate(Predicate<Integer> uidPredicate) {
+        mUidPredicate = uidPredicate;
+    }
+
+    /**
      * Get the CPU frequencies that correspond to the times reported in
      * {@link ThreadCpuUsage#usageTimesMillis}
      */
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
new file mode 100644
index 0000000..77f6a17
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Range;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Service that handles settings for {@link KernelCpuThreadReader}
+ *
+ * <p>N.B.: The `collected_uids` setting takes a string representation of what UIDs to collect data
+ * for. A string representation is used as we will want to express UID ranges, therefore an integer
+ * array could not be used. The format of the string representation is detailed here: {@link
+ * UidPredicate#fromString}.
+ *
+ * @hide Only for use within the system server
+ */
+public class KernelCpuThreadReaderSettingsObserver extends ContentObserver {
+    private static final String TAG = "KernelCpuThreadReaderSettingsObserver";
+
+    /**
+     * The number of frequency buckets to report
+     */
+    private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets";
+    private static final int NUM_BUCKETS_DEFAULT = 8;
+
+    /**
+     * List of UIDs to report data for
+     */
+    private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
+    private static final String COLLECTED_UIDS_DEFAULT = "1000-1000";
+
+    private final Context mContext;
+
+    @Nullable
+    private final KernelCpuThreadReader mKernelCpuThreadReader;
+
+    /**
+     * @return returns a created {@link KernelCpuThreadReader} that will be modified by any
+     * change in settings, returns null if creation failed
+     */
+    @Nullable
+    public static KernelCpuThreadReader getSettingsModifiedReader(Context context) {
+        // Create the observer
+        KernelCpuThreadReaderSettingsObserver settingsObserver =
+                new KernelCpuThreadReaderSettingsObserver(context);
+        // Register the observer to listen for setting changes
+        Uri settingsUri =
+                Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
+        context.getContentResolver().registerContentObserver(
+                settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
+        // Return the observer's reader
+        return settingsObserver.mKernelCpuThreadReader;
+    }
+
+    private KernelCpuThreadReaderSettingsObserver(Context context) {
+        super(BackgroundThread.getHandler());
+        mContext = context;
+        mKernelCpuThreadReader = KernelCpuThreadReader.create(
+                NUM_BUCKETS_DEFAULT,
+                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT));
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri, int userId) {
+        updateReader();
+    }
+
+    /**
+     * Update the reader with new settings
+     */
+    private void updateReader() {
+        if (mKernelCpuThreadReader == null) {
+            return;
+        }
+
+        final KeyValueListParser parser = new KeyValueListParser(',');
+        try {
+            parser.setString(Settings.Global.getString(
+                    mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER));
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Bad settings", e);
+            return;
+        }
+
+        final UidPredicate uidPredicate;
+        try {
+            uidPredicate = UidPredicate.fromString(
+                    parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed to get UID predicate", e);
+            return;
+        }
+
+        mKernelCpuThreadReader.setNumBuckets(
+                parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
+        mKernelCpuThreadReader.setUidPredicate(uidPredicate);
+    }
+
+    /**
+     * Check whether a UID belongs to a set of UIDs
+     */
+    @VisibleForTesting
+    public static class UidPredicate implements Predicate<Integer> {
+        private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)");
+        private static final String UID_SPECIFIER_DELIMITER = ";";
+        private final List<Range<Integer>> mAcceptedUidRanges;
+
+        /**
+         * Create a UID predicate from a string representing a list of UID ranges
+         *
+         * <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single
+         * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by
+         * a single ';'. For example, this would be a valid string representation: {@code
+         * "1000-1999;2003-2003;2004-2004;2050-2060"}.
+         *
+         * <p>We do not use ',' to delimit as it is already used in separating different setting
+         * arguments.
+         *
+         * @throws NumberFormatException    if the input string is incorrectly formatted
+         * @throws IllegalArgumentException if an UID range has a lower end than start
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public static UidPredicate fromString(String predicateString) throws NumberFormatException {
+            final List<Range<Integer>> acceptedUidRanges = new ArrayList<>();
+            for (String uidSpecifier : predicateString.split(UID_SPECIFIER_DELIMITER)) {
+                final Matcher uidRangeMatcher = UID_RANGE_PATTERN.matcher(uidSpecifier);
+                if (!uidRangeMatcher.matches()) {
+                    throw new NumberFormatException(
+                            "Failed to recognize as number range: " + uidSpecifier);
+                }
+                acceptedUidRanges.add(Range.create(
+                        Integer.parseInt(uidRangeMatcher.group(1)),
+                        Integer.parseInt(uidRangeMatcher.group(2))));
+            }
+            return new UidPredicate(acceptedUidRanges);
+        }
+
+        private UidPredicate(List<Range<Integer>> acceptedUidRanges) {
+            mAcceptedUidRanges = acceptedUidRanges;
+        }
+
+        @Override
+        public boolean test(Integer uid) {
+            for (int i = 0; i < mAcceptedUidRanges.size(); i++) {
+                if (mAcceptedUidRanges.get(i).contains(uid)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
new file mode 100644
index 0000000..9283105
--- /dev/null
+++ b/core/java/com/android/internal/os/OWNERS
@@ -0,0 +1 @@
+per-file ZygoteArguments.java,ZygoteConnection.java,ZygoteInit.java,Zygote.java,ZygoteServer.java = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 474d4d7..b881aef 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -94,6 +94,11 @@
      */
     public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
 
+    /**
+     * Enable profiling from shell.
+     */
+    public static final int PROFILE_FROM_SHELL = 1 << 15;
+
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 51a3b48..e7a1c49 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -27,10 +27,6 @@
         "-Wno-error=deprecated-declarations",
         "-Wunused",
         "-Wunreachable-code",
-
-        // TODO: Linear blending should be enabled by default, but we are
-        // TODO: making it an opt-in while it's a work in progress
-        //"-DANDROID_ENABLE_LINEAR_BLENDING",
     ],
 
     cppflags: ["-Wno-conversion-null"],
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index a365a56..86342c4 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -4,3 +4,6 @@
 
 # Connectivity
 per-file android_net_* = ek@google.com, lorenzo@google.com, satk@google.com
+
+# Zygote
+per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index d215f96..e817217 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1149,19 +1149,6 @@
     return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
 }
 
-static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
-    sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
-    // To support any color space, we need to pass an additional ColorSpace argument to
-    // java Bitmap.createHardwareBitmap.
-    SkColorType ct = uirenderer::PixelFormatToColorType(buffer->getPixelFormat());
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, ct, SkColorSpace::MakeSRGB());
-    if (!bitmap.get()) {
-        ALOGW("failed to create hardware bitmap from graphic buffer");
-        return NULL;
-    }
-    return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
-}
-
 static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
                                                jlong colorSpacePtr) {
     AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
@@ -1257,8 +1244,6 @@
     {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
     {   "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
         (void*)Bitmap_copyPreserveInternalConfig },
-    {   "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;",
-        (void*) Bitmap_createHardwareBitmap },
     {   "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
         (void*) Bitmap_wrapHardwareBufferBitmap },
     {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 3f9ec45..79bfa17 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -15,6 +15,7 @@
 #include "Utils.h"
 #include "core_jni_helpers.h"
 
+#include <HardwareBitmapUploader.h>
 #include <nativehelper/JNIHelp.h>
 #include <androidfw/Asset.h>
 #include <androidfw/ResourceTypes.h>
@@ -278,6 +279,11 @@
 
     // Set the decode colorType
     SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
+    if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        decodeColorType = kN32_SkColorType;
+    }
+
     sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
             decodeColorType, prefColorSpace);
 
@@ -354,13 +360,7 @@
     const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
             decodeColorType, alphaType, decodeColorSpace);
 
-    // For wide gamut images, we will leave the color space on the SkBitmap.  Otherwise,
-    // use the default.
     SkImageInfo bitmapInfo = decodeInfo;
-    if (decodeInfo.colorSpace() && decodeInfo.colorSpace()->isSRGB()) {
-        bitmapInfo = bitmapInfo.makeColorSpace(decodeInfo.refColorSpace());
-    }
-
     if (decodeColorType == kGray_8_SkColorType) {
         // The legacy implementation of BitmapFactory used kAlpha8 for
         // grayscale images (before kGray8 existed).  While the codec
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index b4ba749..d65f324 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -33,6 +33,7 @@
 #include "android_util_Binder.h"
 #include "core_jni_helpers.h"
 
+#include <HardwareBitmapUploader.h>
 #include <nativehelper/JNIHelp.h>
 #include <androidfw/Asset.h>
 #include <binder/Parcel.h>
@@ -166,6 +167,10 @@
 
     SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
     SkColorType decodeColorType = brd->computeOutputColorType(colorType);
+    if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        decodeColorType = kN32_SkColorType;
+    }
 
     // Set up the pixel allocator
     SkBRDAllocator* allocator = nullptr;
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 72e14e7..2d83ac3 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -24,6 +24,7 @@
 #include "core_jni_helpers.h"
 
 #include <hwui/Bitmap.h>
+#include <HardwareBitmapUploader.h>
 
 #include <SkAndroidCodec.h>
 #include <SkEncodedImageFormat.h>
@@ -256,6 +257,17 @@
         // This is currently the only way to know that we should decode to F16.
         colorType = codec->computeOutputColorType(colorType);
     }
+
+    const bool isHardware = !requireMutable
+        && (allocator == ImageDecoder::kDefault_Allocator ||
+            allocator == ImageDecoder::kHardware_Allocator)
+        && colorType != kGray_8_SkColorType;
+
+    if (colorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        colorType = kN32_SkColorType;
+    }
+
     sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
     decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
@@ -449,10 +461,7 @@
     if (requireMutable) {
         bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable;
     } else {
-        if ((allocator == ImageDecoder::kDefault_Allocator ||
-             allocator == ImageDecoder::kHardware_Allocator)
-            && bm.colorType() != kAlpha_8_SkColorType)
-        {
+        if (isHardware) {
             sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
             if (hwBitmap) {
                 hwBitmap->setImmutable();
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index ed6a84b..298f7f8 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -8,6 +8,8 @@
 
 #include <jni.h>
 
+#include <vector>
+
 using namespace android::uirenderer;
 
 /**
@@ -18,11 +20,11 @@
  */
 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
 
-static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
-    if (NULL == ptr) {
-        doThrowIAE(env);
+#define ThrowIAE_IfNull(env, ptr)   \
+    if (nullptr == ptr) {           \
+        doThrowIAE(env);            \
+        return 0;                   \
     }
-}
 
 static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
 {
@@ -76,186 +78,115 @@
     }
     sk_sp<SkShader> shader = image->makeShader(
             (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY);
+    ThrowIAE_IfNull(env, shader.get());
 
     if (matrix) {
         shader = shader->makeWithLocalMatrix(*matrix);
     }
 
-    ThrowIAE_IfNull(env, shader.get());
     return reinterpret_cast<jlong>(shader.release());
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr,
-        jfloat x0, jfloat y0, jfloat x1, jfloat y1,
-        jintArray colorArray, jfloatArray posArray, jint tileMode) {
-    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    SkPoint pts[2];
-    pts[0].set(x0, y0);
-    pts[1].set(x1, y1);
+static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
+    const size_t count = env->GetArrayLength(colorArray);
+    const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
 
-    size_t count = env->GetArrayLength(colorArray);
-    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
-    AutoJavaFloatArray autoPos(env, posArray, count);
-#ifdef SK_SCALAR_IS_FLOAT
-    SkScalar* pos = autoPos.ptr();
-#else
-    #error Need to convert float array to SkScalar array before calling the following function.
-#endif
-
-    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts,
-            reinterpret_cast<const SkColor*>(colorValues), pos, count,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
-
-    SkShader* shader;
-    if (matrix) {
-        shader = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        shader = baseShader.release();
+    std::vector<SkColor4f> colors(count);
+    for (size_t i = 0; i < count; ++i) {
+        colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
     }
 
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
-    ThrowIAE_IfNull(env, shader);
-    return reinterpret_cast<jlong>(shader);
-}
-
-static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr,
-        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
-    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-
-    SkPoint pts[2];
-    pts[0].set(x0, y0);
-    pts[1].set(x1, y1);
-
-    SkColor colors[2];
-    colors[0] = color0;
-    colors[1] = color1;
-
-    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
-
-    SkShader* s;
-    if (matrix) {
-        s = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        s = baseShader.release();
-    }
-
-    ThrowIAE_IfNull(env, s);
-    return reinterpret_cast<jlong>(s);
+    env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
+    return colors;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
-        jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
-    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    SkPoint center;
-    center.set(x, y);
+static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
+        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
+        jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
+    SkPoint pts[2];
+    pts[0].set(x0, y0);
+    pts[1].set(x1, y1);
 
-    size_t      count = env->GetArrayLength(colorArray);
-    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
 
-    AutoJavaFloatArray autoPos(env, posArray, count);
+    AutoJavaFloatArray autoPos(env, posArray, colors.size());
 #ifdef SK_SCALAR_IS_FLOAT
     SkScalar* pos = autoPos.ptr();
 #else
     #error Need to convert float array to SkScalar array before calling the following function.
 #endif
 
-    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius,
-            reinterpret_cast<const SkColor*>(colorValues), pos, count,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+    sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
+                GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+                static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr));
+    ThrowIAE_IfNull(env, shader);
 
-    SkShader* shader;
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     if (matrix) {
-        shader = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        shader = baseShader.release();
+        shader = shader->makeWithLocalMatrix(*matrix);
     }
 
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
-                                 JNI_ABORT);
-
-    ThrowIAE_IfNull(env, shader);
-    return reinterpret_cast<jlong>(shader);
+    return reinterpret_cast<jlong>(shader.release());
 }
 
-static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, jfloat radius,
-        jint color0, jint color1, jint tileMode) {
-    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+        jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
+        jlong colorSpaceHandle) {
     SkPoint center;
     center.set(x, y);
 
-    SkColor colors[2];
-    colors[0] = color0;
-    colors[1] = color1;
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
 
-    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+    AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar* pos = autoPos.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
 
-    SkShader* shader;
-    if (matrix) {
-        shader = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        shader = baseShader.release();
-    }
+    sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
+            GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr);
     ThrowIAE_IfNull(env, shader);
-    return reinterpret_cast<jlong>(shader);
+
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    if (matrix) {
+        shader = shader->makeWithLocalMatrix(*matrix);
+    }
+
+    return reinterpret_cast<jlong>(shader.release());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
-        jintArray jcolors, jfloatArray jpositions) {
-    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    size_t      count = env->GetArrayLength(jcolors);
-    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
+static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+        jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
 
-    AutoJavaFloatArray autoPos(env, jpositions, count);
+    AutoJavaFloatArray autoPos(env, jpositions, colors.size());
 #ifdef SK_SCALAR_IS_FLOAT
     SkScalar* pos = autoPos.ptr();
 #else
     #error Need to convert float array to SkScalar array before calling the following function.
 #endif
 
-    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y,
-            reinterpret_cast<const SkColor*>(colors), pos, count,
-            sGradientShaderFlags, NULL);
-
-    SkShader* shader;
-    if (matrix) {
-        shader = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        shader = baseShader.release();
-    }
-
-    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
-                                 JNI_ABORT);
+    sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
+            GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+            sGradientShaderFlags, nullptr);
     ThrowIAE_IfNull(env, shader);
-    return reinterpret_cast<jlong>(shader);
-}
 
-static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
-        int color0, int color1) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    SkColor colors[2];
-    colors[0] = color0;
-    colors[1] = color1;
-
-    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, colors,
-            NULL, 2, sGradientShaderFlags, NULL);
-
-    SkShader* shader;
     if (matrix) {
-        shader = baseShader->makeWithLocalMatrix(*matrix).release();
-    } else {
-        shader = baseShader.release();
+        shader = shader->makeWithLocalMatrix(*matrix);
     }
-    ThrowIAE_IfNull(env, shader);
-    return reinterpret_cast<jlong>(shader);
+
+    return reinterpret_cast<jlong>(shader.release());
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -295,18 +226,15 @@
 };
 
 static const JNINativeMethod gLinearGradientMethods[] = {
-    { "nativeCreate1",     "(JFFFF[I[FI)J",  (void*)LinearGradient_create1     },
-    { "nativeCreate2",     "(JFFFFIII)J",    (void*)LinearGradient_create2     },
+    { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
 };
 
 static const JNINativeMethod gRadialGradientMethods[] = {
-    { "nativeCreate1",     "(JFFF[I[FI)J",  (void*)RadialGradient_create1     },
-    { "nativeCreate2",     "(JFFFIII)J",    (void*)RadialGradient_create2     },
+    { "nativeCreate",     "(JFFF[J[FIJ)J",  (void*)RadialGradient_create     },
 };
 
 static const JNINativeMethod gSweepGradientMethods[] = {
-    { "nativeCreate1",     "(JFF[I[F)J",  (void*)SweepGradient_create1     },
-    { "nativeCreate2",     "(JFFII)J",    (void*)SweepGradient_create2     },
+    { "nativeCreate",     "(JFF[J[FJ)J",  (void*)SweepGradient_create     },
 };
 
 static const JNINativeMethod gComposeShaderMethods[] = {
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index 2d1fec8..00e5ba3 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -21,6 +21,7 @@
 
 #include "android_os_Parcel.h"
 #include "android/graphics/GraphicsJNI.h"
+#include "android/graphics/GraphicBuffer.h"
 
 #include <android/hardware_buffer.h>
 #include <android_runtime/android_hardware_HardwareBuffer.h>
@@ -96,6 +97,12 @@
     return reinterpret_cast<jlong>(wrapper);
 }
 
+static jlong android_hardware_HardwareBuffer_createFromGraphicBuffer(JNIEnv* env, jobject clazz, jobject graphicBuffer) {
+    sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
+    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
+    return reinterpret_cast<jlong>(wrapper);
+}
+
 static void destroyWrapper(GraphicBufferWrapper* wrapper) {
     delete wrapper;
 }
@@ -243,6 +250,8 @@
 static const JNINativeMethod gMethods[] = {
     { "nCreateHardwareBuffer",  "(IIIIJ)J",
             (void*) android_hardware_HardwareBuffer_create },
+    { "nCreateFromGraphicBuffer", "(Landroid/graphics/GraphicBuffer;)J",
+            (void*) android_hardware_HardwareBuffer_createFromGraphicBuffer },
     { "nGetNativeFinalizer", "()J",
             (void*) android_hardware_HardwareBuffer_getNativeFinalizer },
     { "nWriteHardwareBufferToParcel",  "(JLandroid/os/Parcel;)V",
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 1ab5843..b9301d4 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "InputApplicationHandle"
 
 #include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
 #include "jni.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
@@ -63,16 +64,7 @@
         mInfo = new InputApplicationInfo();
     }
 
-    jstring nameObj = jstring(env->GetObjectField(obj,
-            gInputApplicationHandleClassInfo.name));
-    if (nameObj) {
-        const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo->name = nameStr;
-        env->ReleaseStringUTFChars(nameObj, nameStr);
-        env->DeleteLocalRef(nameObj);
-    } else {
-        mInfo->name = "<null>";
-    }
+    mInfo->name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
 
     mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 67a7441..eb71052 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "InputWindowHandle"
 
 #include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
 #include "jni.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
@@ -86,24 +87,14 @@
 
     mInfo.touchableRegion.clear();
 
-    jobject tokenObj = env->GetObjectField(obj,
-            gInputWindowHandleClassInfo.token);
+    jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);
     if (tokenObj) {
         mInfo.token = ibinderForJavaObject(env, tokenObj);
     } else {
         mInfo.token.clear();
     }
 
-    jstring nameObj = jstring(env->GetObjectField(obj,
-            gInputWindowHandleClassInfo.name));
-    if (nameObj) {
-        const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo.name = nameStr;
-        env->ReleaseStringUTFChars(nameObj, nameStr);
-        env->DeleteLocalRef(nameObj);
-    } else {
-        mInfo.name = "<null>";
-    }
+    mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
 
     mInfo.layoutParamsFlags = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsFlags);
@@ -241,8 +232,7 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
             "ptr", "J");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
-            clazz,
+    GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, clazz,
             "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp
index 4be4def..b87a34d 100644
--- a/core/jni/android_media_AudioAttributes.cpp
+++ b/core/jni/android_media_AudioAttributes.cpp
@@ -135,7 +135,7 @@
 {
     audio_attributes_t *aa = new (calloc(1, sizeof(audio_attributes_t)))
                 audio_attributes_t{AUDIO_ATTRIBUTES_INITIALIZER};
-    return UniqueAaPtr{aa, free};
+    return UniqueAaPtr{aa};
 }
 
 jint JNIAudioAttributeHelper::nativeFromJava(JNIEnv* env, jobject jAudioAttributes,
diff --git a/core/jni/android_media_AudioAttributes.h b/core/jni/android_media_AudioAttributes.h
index c558352..628f7e3 100644
--- a/core/jni/android_media_AudioAttributes.h
+++ b/core/jni/android_media_AudioAttributes.h
@@ -27,7 +27,11 @@
 class JNIAudioAttributeHelper
 {
 public:
-    using UniqueAaPtr = std::unique_ptr<audio_attributes_t, decltype(free)*>;
+    struct FreeDeleter {
+        void operator()(void *p) const { ::free(p); }
+    };
+
+    using UniqueAaPtr = std::unique_ptr<audio_attributes_t, FreeDeleter>;
 
     /**
      * @brief makeUnique helper to prevent leak
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 34abcd8..a2ed7d3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -621,6 +621,24 @@
 }
 
 static jint
+android_media_AudioSystem_setMasterBalance(JNIEnv *env, jobject thiz, jfloat balance)
+{
+    return (jint) check_AudioSystem_Command(AudioSystem::setMasterBalance(balance));
+}
+
+static jfloat
+android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz)
+{
+    float balance;
+    const status_t status = AudioSystem::getMasterBalance(&balance);
+    if (status != NO_ERROR) {
+        ALOGW("%s getMasterBalance error %d, returning 0.f, audioserver down?", __func__, status);
+        balance = 0.f;
+    }
+    return balance;
+}
+
+static jint
 android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
 {
     return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
@@ -2160,6 +2178,8 @@
     {"getMasterMute",       "()Z",      (void *)android_media_AudioSystem_getMasterMute},
     {"setMasterMono",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMono},
     {"getMasterMono",       "()Z",      (void *)android_media_AudioSystem_getMasterMono},
+    {"setMasterBalance",    "(F)I",     (void *)android_media_AudioSystem_setMasterBalance},
+    {"getMasterBalance",    "()F",      (void *)android_media_AudioSystem_getMasterBalance},
     {"getDevicesForStream", "(I)I",     (void *)android_media_AudioSystem_getDevicesForStream},
     {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
     {"getPrimaryOutputFrameCount",   "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index e64d2af..ee11b61 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -96,7 +96,7 @@
     return toJavaStringArray(env, cStrings);
 }
 
-static jint verify(JNIEnv* env, jobjectArray packageInfo, android::vintf::CheckFlags::Type checks) {
+static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray packageInfo) {
     std::vector<std::string> cPackageInfo;
     if (packageInfo) {
         size_t count = env->GetArrayLength(packageInfo);
@@ -109,18 +109,19 @@
         }
     }
     std::string error;
-    int32_t status = VintfObject::CheckCompatibility(cPackageInfo, &error, checks);
+    int32_t status = VintfObject::CheckCompatibility(cPackageInfo, &error);
     if (status)
         LOG(WARNING) << "VintfObject.verify() returns " << status << ": " << error;
     return status;
 }
 
-static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray packageInfo) {
-    return verify(env, packageInfo, ::android::vintf::CheckFlags::ENABLE_ALL_CHECKS);
-}
-
 static jint android_os_VintfObject_verifyWithoutAvb(JNIEnv* env, jclass) {
-    return verify(env, nullptr, ::android::vintf::CheckFlags::DISABLE_AVB_CHECK);
+    std::string error;
+    int32_t status = VintfObject::CheckCompatibility({}, &error,
+            ::android::vintf::CheckFlags::DISABLE_AVB_CHECK);
+    if (status)
+        LOG(WARNING) << "VintfObject.verifyWithoutAvb() returns " << status << ": " << error;
+    return status;
 }
 
 static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index fb6be6b..9819d9a 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "InputChannel-JNI"
 
 #include <nativehelper/JNIHelp.h>
-
+#include "nativehelper/scoped_utf_chars.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <binder/Parcel.h>
 #include <utils/Log.h>
@@ -123,9 +123,8 @@
 
 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
         jclass clazz, jstring nameObj) {
-    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
-    std::string name = nameChars;
-    env->ReleaseStringUTFChars(nameObj, nameChars);
+    ScopedUtfChars nameChars(env, nameObj);
+    std::string name = nameChars.c_str();
 
     sp<InputChannel> serverChannel;
     sp<InputChannel> clientChannel;
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 0d75de9..ce5512b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -507,15 +507,15 @@
 jmethodID gPositionListener_PositionLostMethod;
 
 static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
-        jlong renderNodePtr, jobject surfaceview) {
-    class SurfaceViewPositionUpdater : public RenderNode::PositionListener {
+        jlong renderNodePtr, jobject listener) {
+    class PositionListenerTrampoline : public RenderNode::PositionListener {
     public:
-        SurfaceViewPositionUpdater(JNIEnv* env, jobject surfaceview) {
+        PositionListenerTrampoline(JNIEnv* env, jobject listener) {
             env->GetJavaVM(&mVm);
-            mWeakRef = env->NewWeakGlobalRef(surfaceview);
+            mWeakRef = env->NewWeakGlobalRef(listener);
         }
 
-        virtual ~SurfaceViewPositionUpdater() {
+        virtual ~PositionListenerTrampoline() {
             jnienv()->DeleteWeakGlobalRef(mWeakRef);
             mWeakRef = nullptr;
         }
@@ -539,9 +539,14 @@
                 bounds.roundOut();
             }
 
+            if (mPreviousPosition == bounds) {
+                return;
+            }
+            mPreviousPosition = bounds;
+
             incStrong(0);
             auto functor = std::bind(
-                std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
+                std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this,
                 (jlong) info.canvasContext.getFrameNumber(),
                 (jint) bounds.left, (jint) bounds.top,
                 (jint) bounds.right, (jint) bounds.bottom);
@@ -552,6 +557,11 @@
         virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
             if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
 
+            if (mPreviousPosition.isEmpty()) {
+                return;
+            }
+            mPreviousPosition.setEmpty();
+
             ATRACE_NAME("SurfaceView position lost");
             JNIEnv* env = jnienv();
             jobject localref = env->NewLocalRef(mWeakRef);
@@ -561,6 +571,7 @@
                 return;
             }
 
+            // TODO: Remember why this is synchronous and then make a comment
             env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
                     info ? info->canvasContext.getFrameNumber() : 0);
             env->DeleteLocalRef(localref);
@@ -596,10 +607,11 @@
 
         JavaVM* mVm;
         jobject mWeakRef;
+        uirenderer::Rect mPreviousPosition;
     };
 
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    renderNode->setPositionListener(new SurfaceViewPositionUpdater(env, surfaceview));
+    renderNode->setPositionListener(new PositionListenerTrampoline(env, listener));
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f1b259e..fad2fe0 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -36,6 +36,7 @@
 #include <memory>
 #include <stdio.h>
 #include <system/graphics.h>
+#include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
@@ -108,6 +109,23 @@
     jmethodID ctor;
 } gDisplayedContentSamplingAttributesClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+    jfieldID X;
+    jfieldID Y;
+    jfieldID Z;
+} gCieXyzClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+    jfieldID red;
+    jfieldID green;
+    jfieldID blue;
+    jfieldID white;
+} gDisplayPrimariesClassInfo;
+
 // ----------------------------------------------------------------------------
 
 static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -688,6 +706,66 @@
     return colorModesArray;
 }
 
+static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return NULL;
+
+    ui::DisplayPrimaries primaries;
+    if (SurfaceComposerClient::getDisplayNativePrimaries(token, primaries) != NO_ERROR) {
+        return NULL;
+    }
+
+    jobject jred = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+    if (jred == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    jobject jgreen = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+    if (jgreen == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    jobject jblue = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+    if (jblue == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    jobject jwhite = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+    if (jwhite == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    jobject jprimaries = env->NewObject(gDisplayPrimariesClassInfo.clazz,
+            gDisplayPrimariesClassInfo.ctor);
+    if (jprimaries == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    env->SetFloatField(jred, gCieXyzClassInfo.X, primaries.red.X);
+    env->SetFloatField(jred, gCieXyzClassInfo.Y, primaries.red.Y);
+    env->SetFloatField(jred, gCieXyzClassInfo.Z, primaries.red.Z);
+    env->SetFloatField(jgreen, gCieXyzClassInfo.X, primaries.green.X);
+    env->SetFloatField(jgreen, gCieXyzClassInfo.Y, primaries.green.Y);
+    env->SetFloatField(jgreen, gCieXyzClassInfo.Z, primaries.green.Z);
+    env->SetFloatField(jblue, gCieXyzClassInfo.X, primaries.blue.X);
+    env->SetFloatField(jblue, gCieXyzClassInfo.Y, primaries.blue.Y);
+    env->SetFloatField(jblue, gCieXyzClassInfo.Z, primaries.blue.Z);
+    env->SetFloatField(jwhite, gCieXyzClassInfo.X, primaries.white.X);
+    env->SetFloatField(jwhite, gCieXyzClassInfo.Y, primaries.white.Y);
+    env->SetFloatField(jwhite, gCieXyzClassInfo.Z, primaries.white.Z);
+    env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.red, jred);
+    env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.green, jgreen);
+    env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.blue, jblue);
+    env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.white, jwhite);
+
+    return jprimaries;
+}
+
 static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return -1;
@@ -1089,6 +1167,8 @@
             (void*)nativeSetActiveConfig },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
+    {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
+            (void*)nativeGetDisplayNativePrimaries },
     {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I",
             (void*)nativeGetActiveColorMode},
     {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
@@ -1209,6 +1289,28 @@
             displayedContentSamplingAttributesClazz);
     gDisplayedContentSamplingAttributesClassInfo.ctor = GetMethodIDOrDie(env,
             displayedContentSamplingAttributesClazz, "<init>", "(III)V");
+
+    jclass cieXyzClazz = FindClassOrDie(env, "android/view/SurfaceControl$CieXyz");
+    gCieXyzClassInfo.clazz = MakeGlobalRefOrDie(env, cieXyzClazz);
+    gCieXyzClassInfo.ctor = GetMethodIDOrDie(env, gCieXyzClassInfo.clazz, "<init>", "()V");
+    gCieXyzClassInfo.X = GetFieldIDOrDie(env, cieXyzClazz, "X", "F");
+    gCieXyzClassInfo.Y = GetFieldIDOrDie(env, cieXyzClazz, "Y", "F");
+    gCieXyzClassInfo.Z = GetFieldIDOrDie(env, cieXyzClazz, "Z", "F");
+
+    jclass displayPrimariesClazz = FindClassOrDie(env,
+            "android/view/SurfaceControl$DisplayPrimaries");
+    gDisplayPrimariesClassInfo.clazz = MakeGlobalRefOrDie(env, displayPrimariesClazz);
+    gDisplayPrimariesClassInfo.ctor = GetMethodIDOrDie(env, gDisplayPrimariesClassInfo.clazz,
+            "<init>", "()V");
+    gDisplayPrimariesClassInfo.red = GetFieldIDOrDie(env, displayPrimariesClazz, "red",
+            "Landroid/view/SurfaceControl$CieXyz;");
+    gDisplayPrimariesClassInfo.green = GetFieldIDOrDie(env, displayPrimariesClazz, "green",
+            "Landroid/view/SurfaceControl$CieXyz;");
+    gDisplayPrimariesClassInfo.blue = GetFieldIDOrDie(env, displayPrimariesClazz, "blue",
+            "Landroid/view/SurfaceControl$CieXyz;");
+    gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
+            "Landroid/view/SurfaceControl$CieXyz;");
+
     return err;
 }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6ee9606..b7837ef 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -276,6 +276,7 @@
 // Must match values in com.android.internal.os.Zygote.
 enum RuntimeFlags : uint32_t {
   DEBUG_ENABLE_JDWP = 1,
+  PROFILE_FROM_SHELL = 1 << 15,
 };
 
 // Forward declaration so we don't have to move the signal handler.
@@ -1224,6 +1225,13 @@
   if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
     EnableDebugger();
   }
+  if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
+    // simpleperf needs the process to be dumpable to profile it.
+    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+      ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+    }
+  }
 
   if (NeedsNoRandomizeWorkaround()) {
     // Work around ARM kernel ASLR lossage (http://b/5817320).
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index 1325b0c..16ef753 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -18,6 +18,8 @@
 #define CORE_JNI_HELPERS
 
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <nativehelper/scoped_utf_chars.h>
 #include <android_runtime/AndroidRuntime.h>
 
 namespace android {
@@ -72,6 +74,20 @@
     return res;
 }
 
+/**
+ * Read the specified field from jobject, and convert to std::string.
+ * If the field cannot be obtained, return defaultValue.
+ */
+static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fieldId,
+        const char* defaultValue) {
+    ScopedLocalRef<jstring> strObj(env, jstring(env->GetObjectField(obj, fieldId)));
+    if (strObj != nullptr) {
+        ScopedUtfChars chars(env, strObj.get());
+        return std::string(chars.c_str());
+    }
+    return std::string(defaultValue);
+}
+
 }  // namespace android
 
 #endif  // CORE_JNI_HELPERS
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 94a6734..30b9b78 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -595,6 +595,21 @@
 
     // ACTION: Tap & Pay -> Default Application Setting -> Use Default
     ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
+
+    // ACTION: Settings > Search Bar > Avatar
+    // CATEGORY: SETTINGS
+    // OS: Q
+    CLICK_ACCOUNT_AVATAR = 1643;
+
+    // ACTION: Set new password (action intent android.app.action.SET_NEW_PASSWORD)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_SET_NEW_PASSWORD = 1645;
+
+    // ACTION: Set new password (action intent android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646;
 }
 
 /**
@@ -2189,4 +2204,14 @@
 
     // OPEN: Settings > System > Aware > Disable > Dialog
     DIALOG_AWARE_DISABLE = 1633;
+
+    // OPEN: Settings > Settings > Network & internet > Click Mobile network to land on page with
+    // details for a SIM/eSIM mobile network > Click edit icon to bring up a rename dialog.
+    // OS: Q
+    MOBILE_NETWORK_RENAME_DIALOG = 1642;
+
+    // OPEN: Set new password (android.app.action.SET_NEW_PASSWORD action intent)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SET_NEW_PASSWORD_ACTIVITY = 1644;
 }
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index dfc4081..41a7498 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -232,6 +232,8 @@
         // Which streams are affected by mute. The stream type's bit should be set
         // to 1 if it should be muted when a mute request is received.
         optional SettingProto mute_streams_affected = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Master balance (float -1.f = 100% left, 0.f = dead center, 1.f = 100% right).
+        optional SettingProto master_balance = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Volume volume = 33;
 
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index af0a942..8fce94e 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -318,6 +318,16 @@
     // Whether battery saver is enabled.
     optional bool enabled = 1;
 
+    // Whether full battery saver is enabled.
+    optional bool is_full_enabled = 14;
+
+    // Whether adaptive battery saver is enabled.
+    optional bool is_adaptive_enabled = 15;
+
+    // Whether the battery saver policy indicates that is_enabled should be
+    // advertised.
+    optional bool should_advertise_is_enabled = 16;
+
     // Whether system has booted.
     optional bool boot_completed = 2;
 
@@ -358,4 +368,10 @@
     // The value of Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL. This is a cached value, so it
     // could be slightly different from what's in GlobalSettingsProto.LowPowerMode.
     optional int32 setting_battery_saver_sticky_auto_disable_threshold = 13;
+
+    // The last time adaptive battery saver was changed by an external service,
+    // using elapsed realtime as the timebase.
+    optional int64 last_adaptive_battery_saver_changed_externally_elapsed = 17;
+
+    // Next tag: 18
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7a261a9..840291d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -274,6 +274,7 @@
     <protected-broadcast android:name="android.media.MASTER_VOLUME_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.MASTER_MUTE_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.MASTER_MONO_CHANGED_ACTION" />
+    <protected-broadcast android:name="android.media.MASTER_BALANCE_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
     <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
 
@@ -1730,7 +1731,7 @@
     <permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows a system application to access hardware packet offload capabilities.
+    <!-- @SystemApi Allows a system application to access hardware packet offload capabilities.
          @hide -->
     <permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
         android:protectionLevel="signature|privileged" />
@@ -1868,6 +1869,10 @@
     <permission android:name="android.permission.HARDWARE_TEST"
         android:protectionLevel="signature" />
 
+    <!-- @hide Allows an application to manage DynamicAndroid image -->
+    <permission android:name="android.permission.MANAGE_DYNAMIC_ANDROID"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows access to Broadcast Radio
          @hide This is not a third-party API (intended for system apps).-->
     <permission android:name="android.permission.ACCESS_BROADCAST_RADIO"
@@ -4328,7 +4333,7 @@
     <!-- @SystemApi Allows an application to manage the app predictions service.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_APP_PREDICTIONS"
-         android:protectionLevel="signature" />
+         android:protectionLevel="signature|appPredictor" />
 
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
diff --git a/core/res/res/anim/lock_in.xml b/core/res/res/anim/lock_in.xml
new file mode 100755
index 0000000..992bc92
--- /dev/null
+++ b/core/res/res/anim/lock_in.xml
@@ -0,0 +1,404 @@
+<!--
+     Copyright (C) 2019 The Android Open 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G_N_3_T_0_M">
+                    <group
+                        android:name="_R_G_L_2_G_N_3_T_0"
+                        android:pivotX="2.25"
+                        android:pivotY="2.25"
+                        android:scaleX="0"
+                        android:scaleY="0"
+                        android:translateX="9.75"
+                        android:translateY="12.75">
+                        <group
+                            android:name="_R_G_L_2_G_T_1"
+                            android:scaleX="0.12346"
+                            android:scaleY="0.12346"
+                            android:translateX="2.25"
+                            android:translateY="2.373">
+                            <group
+                                android:name="_R_G_L_2_G"
+                                android:translateY="32">
+                                <path
+                                    android:name="_R_G_L_2_G_D_0_P_0"
+                                    android:fillAlpha="0"
+                                    android:fillColor="#ff0000"
+                                    android:fillType="nonZero"
+                                    android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                                    android:trimPathStart="0.14"
+                                    android:trimPathEnd="0.89"
+                                    android:trimPathOffset="0" />
+                                <path
+                                    android:name="_R_G_L_2_G_D_1_P_0"
+                                    android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                                    android:strokeWidth="16"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="?attr/textColor"
+                                    android:trimPathStart="0.14"
+                                    android:trimPathEnd="0.89"
+                                    android:trimPathOffset="0" />
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotX="8.25"
+                    android:pivotY="7.25"
+                    android:scaleX="0"
+                    android:scaleY="0"
+                    android:translateX="3.75"
+                    android:translateY="7.75">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="?attr/textColor"
+                        android:fillType="nonZero"
+                        android:pathData=" M14.25 0.25 C14.25,0.25 12.75,0.25 12.75,0.25 C12.75,0.25 10.75,0.25 10.75,0.25 C10.75,0.25 5.75,0.25 5.75,0.25 C5.75,0.25 3.75,0.25 3.75,0.25 C3.75,0.25 2.25,0.25 2.25,0.25 C1.15,0.25 0.25,1.15 0.25,2.25 C0.25,2.25 0.25,12.25 0.25,12.25 C0.25,13.35 1.15,14.25 2.25,14.25 C2.25,14.25 14.25,14.25 14.25,14.25 C15.35,14.25 16.25,13.35 16.25,12.25 C16.25,12.25 16.25,2.25 16.25,2.25 C16.25,1.15 15.35,0.25 14.25,0.25c  M14.25 12.25 C14.25,12.25 2.25,12.25 2.25,12.25 C2.25,12.25 2.25,2.25 2.25,2.25 C2.25,2.25 3.75,2.25 3.75,2.25 C3.75,2.25 12.75,2.25 12.75,2.25 C12.75,2.25 14.25,2.25 14.25,2.25 C14.25,2.25 14.25,12.25 14.25,12.25c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:pivotX="2.25"
+                    android:pivotY="2.25"
+                    android:scaleX="0"
+                    android:scaleY="0"
+                    android:translateX="9.75"
+                    android:translateY="12.75">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="?attr/textColor"
+                        android:fillType="nonZero"
+                        android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0.14"
+                    android:valueTo="0.14"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="50"
+                    android:valueFrom="0.14"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0.89"
+                    android:valueTo="0.89"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="50"
+                    android:valueFrom="0.89"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0.14"
+                    android:valueTo="0.14"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="50"
+                    android:valueFrom="0.14"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="0.89"
+                    android:valueTo="0.89"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="50"
+                    android:valueFrom="0.89"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="150"
+                    android:pathData="M 2.25,2.373C 2.25,1.2001604776382402 2.25,-3.49116047763824 2.25,-4.664"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_3_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.0125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.556 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.0125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.556 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleX"
+                    android:startOffset="233"
+                    android:valueFrom="1.0125"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleY"
+                    android:startOffset="233"
+                    android:valueFrom="1.0125"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_3_T_0_M">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleX"
+                    android:startOffset="50"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.025"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.025"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleX"
+                    android:startOffset="233"
+                    android:valueFrom="1.025"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleY"
+                    android:startOffset="233"
+                    android:valueFrom="1.025"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.0125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.556 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1.0125"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.043,0.556 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleX"
+                    android:startOffset="233"
+                    android:valueFrom="1.0125"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="scaleY"
+                    android:startOffset="233"
+                    android:valueFrom="1.0125"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="717"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_lock.xml b/core/res/res/anim/lock_lock.xml
new file mode 100755
index 0000000..8b7887c
--- /dev/null
+++ b/core/res/res/anim/lock_lock.xml
@@ -0,0 +1,16 @@
+<!--
+     Copyright (C) 2019 The Android Open 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="24dp" android:width="24dp" android:viewportHeight="24" android:viewportWidth="24"><group android:name="_R_G"><group android:name="_R_G_L_2_G_T_1" android:translateX="12" android:translateY="15"><group android:name="_R_G_L_2_G" android:translateX="-8.25" android:translateY="-7.25"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="?attr/textColor" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M14.25 0.25 C14.25,0.25 12.75,0.25 12.75,0.25 C12.75,0.25 10.75,0.25 10.75,0.25 C10.75,0.25 5.75,0.25 5.75,0.25 C5.75,0.25 3.75,0.25 3.75,0.25 C3.75,0.25 2.25,0.25 2.25,0.25 C1.15,0.25 0.25,1.15 0.25,2.25 C0.25,2.25 0.25,12.25 0.25,12.25 C0.25,13.35 1.15,14.25 2.25,14.25 C2.25,14.25 14.25,14.25 14.25,14.25 C15.35,14.25 16.25,13.35 16.25,12.25 C16.25,12.25 16.25,2.25 16.25,2.25 C16.25,1.15 15.35,0.25 14.25,0.25c  M14.25 12.25 C14.25,12.25 2.25,12.25 2.25,12.25 C2.25,12.25 2.25,2.25 2.25,2.25 C2.25,2.25 3.75,2.25 3.75,2.25 C3.75,2.25 12.75,2.25 12.75,2.25 C12.75,2.25 14.25,2.25 14.25,2.25 C14.25,2.25 14.25,12.25 14.25,12.25c "/></group></group><group android:name="_R_G_L_1_G_N_5_T_1" android:translateX="12" android:translateY="15"><group android:name="_R_G_L_1_G_N_5_T_0" android:translateX="-8.25" android:translateY="-7.25"><group android:name="_R_G_L_1_G" android:translateX="6" android:translateY="5" android:pivotX="2.25" android:pivotY="2.25" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="?attr/textColor" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c "/></group></group></group><group android:name="_R_G_L_0_G_N_5_T_1" android:translateX="12" android:translateY="15"><group android:name="_R_G_L_0_G_N_5_T_0" android:translateX="-8.25" android:translateY="-7.25"><group android:name="_R_G_L_0_G" android:translateX="-16.219" android:translateY="32.25" android:pivotX="27.965" android:pivotY="-32" android:scaleX="0.125" android:scaleY="0.125"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ff0000" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="?attr/textColor" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="16" android:strokeAlpha="1" android:pathData=" M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 "/></group></group></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_2_G_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="400" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="400" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15.594 12,15.594"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="83" android:startOffset="467" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15.594C 12,15.594 12,15.09895833581686 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="450" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="450" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="450" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="450" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="183" android:startOffset="517" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="183" android:startOffset="517" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_N_5_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="400" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="400" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15.594 12,15.594"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="83" android:startOffset="467" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15.594C 12,15.594 12,15.09895833581686 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="317" android:startOffset="0" android:valueFrom="M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 " android:valueTo="M-27.97 -55.05 C-27.97,-55.05 -28,-60.25 -27.97,-61.98 C-27.92,-64.13 -23.5,-86.37 -0.75,-86.32 C22.77,-86.26 27.75,-65.87 27.72,-64.77 C27.55,-59.38 27.97,-31.67 27.97,-31.67 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.974,0 0.458,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="133" android:startOffset="317" android:valueFrom="M-27.97 -55.05 C-27.97,-55.05 -28,-60.25 -27.97,-61.98 C-27.92,-64.13 -23.5,-86.37 -0.75,-86.32 C22.77,-86.26 27.75,-65.87 27.72,-64.77 C27.55,-59.38 27.97,-31.67 27.97,-31.67 " android:valueTo="M-28.21 -28.42 C-28.21,-28.42 -27.85,-44.88 -27.97,-51.98 C-28,-54.13 -23.5,-76.37 -0.75,-76.32 C22.77,-76.26 27.75,-55.87 27.72,-54.77 C27.55,-49.38 27.97,-28.17 27.97,-28.17 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="250" android:startOffset="450" android:valueFrom="M-28.21 -28.42 C-28.21,-28.42 -27.85,-44.88 -27.97,-51.98 C-28,-54.13 -23.5,-76.37 -0.75,-76.32 C22.77,-76.26 27.75,-55.87 27.72,-54.77 C27.55,-49.38 27.97,-28.17 27.97,-28.17 " android:valueTo="M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.414,0 0.647,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="317" android:startOffset="0" android:valueFrom="M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 " android:valueTo="M-27.97 -55.05 C-27.97,-55.05 -28,-60.25 -27.97,-61.98 C-27.92,-64.13 -23.5,-86.37 -0.75,-86.32 C22.77,-86.26 27.75,-65.87 27.72,-64.77 C27.55,-59.38 27.97,-31.67 27.97,-31.67 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.974,0 0.458,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="133" android:startOffset="317" android:valueFrom="M-27.97 -55.05 C-27.97,-55.05 -28,-60.25 -27.97,-61.98 C-27.92,-64.13 -23.5,-86.37 -0.75,-86.32 C22.77,-86.26 27.75,-65.87 27.72,-64.77 C27.55,-59.38 27.97,-31.67 27.97,-31.67 " android:valueTo="M-28.21 -28.42 C-28.21,-28.42 -27.85,-44.88 -27.97,-51.98 C-28,-54.13 -23.5,-76.37 -0.75,-76.32 C22.77,-76.26 27.75,-55.87 27.72,-54.77 C27.55,-49.38 27.97,-28.17 27.97,-28.17 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="250" android:startOffset="450" android:valueFrom="M-28.21 -28.42 C-28.21,-28.42 -27.85,-44.88 -27.97,-51.98 C-28,-54.13 -23.5,-76.37 -0.75,-76.32 C22.77,-76.26 27.75,-55.87 27.72,-54.77 C27.55,-49.38 27.97,-28.17 27.97,-28.17 " android:valueTo="M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.414,0 0.647,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_N_5_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="400" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="400" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15C 12,15.09895833581686 12,15.594 12,15.594"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="83" android:startOffset="467" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 12,15.594C 12,15.594 12,15.09895833581686 12,15"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="717" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_out.xml b/core/res/res/anim/lock_out.xml
new file mode 100755
index 0000000..2543d47
--- /dev/null
+++ b/core/res/res/anim/lock_out.xml
@@ -0,0 +1,277 @@
+<!--
+     Copyright (C) 2019 The Android Open 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G_N_4_T_0_M">
+                    <group
+                        android:name="_R_G_L_2_G_N_4_T_0"
+                        android:pivotX="8.25"
+                        android:pivotY="7.25"
+                        android:scaleX="1"
+                        android:scaleY="1"
+                        android:translateX="3.75"
+                        android:translateY="7.75">
+                        <group
+                            android:name="_R_G_L_2_G"
+                            android:pivotY="-32"
+                            android:scaleX="0.125"
+                            android:scaleY="0.125"
+                            android:translateX="8.25"
+                            android:translateY="32.267">
+                            <path
+                                android:name="_R_G_L_2_G_D_0_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="#ff0000"
+                                android:fillType="nonZero"
+                                android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                                android:trimPathStart="0"
+                                android:trimPathEnd="1"
+                                android:trimPathOffset="0" />
+                            <path
+                                android:name="_R_G_L_2_G_D_1_P_0"
+                                android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                                android:strokeWidth="16"
+                                android:strokeAlpha="1"
+                                android:strokeColor="?attr/textColor"
+                                android:trimPathStart="0"
+                                android:trimPathEnd="1"
+                                android:trimPathOffset="0" />
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotX="8.25"
+                    android:pivotY="7.25"
+                    android:scaleX="1"
+                    android:scaleY="1"
+                    android:translateX="3.75"
+                    android:translateY="7.75">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="?attr/textColor"
+                        android:fillType="nonZero"
+                        android:pathData=" M14.25 0.25 C14.25,0.25 12.75,0.25 12.75,0.25 C12.75,0.25 10.75,0.25 10.75,0.25 C10.75,0.25 5.75,0.25 5.75,0.25 C5.75,0.25 3.75,0.25 3.75,0.25 C3.75,0.25 2.25,0.25 2.25,0.25 C1.15,0.25 0.25,1.15 0.25,2.25 C0.25,2.25 0.25,12.25 0.25,12.25 C0.25,13.35 1.15,14.25 2.25,14.25 C2.25,14.25 14.25,14.25 14.25,14.25 C15.35,14.25 16.25,13.35 16.25,12.25 C16.25,12.25 16.25,2.25 16.25,2.25 C16.25,1.15 15.35,0.25 14.25,0.25c  M14.25 12.25 C14.25,12.25 2.25,12.25 2.25,12.25 C2.25,12.25 2.25,2.25 2.25,2.25 C2.25,2.25 3.75,2.25 3.75,2.25 C3.75,2.25 12.75,2.25 12.75,2.25 C12.75,2.25 14.25,2.25 14.25,2.25 C14.25,2.25 14.25,12.25 14.25,12.25c " />
+                </group>
+                <group android:name="_R_G_L_0_G_N_4_T_0_M">
+                    <group
+                        android:name="_R_G_L_0_G_N_4_T_0"
+                        android:pivotX="8.25"
+                        android:pivotY="7.25"
+                        android:scaleX="1"
+                        android:scaleY="1"
+                        android:translateX="3.75"
+                        android:translateY="7.75">
+                        <group
+                            android:name="_R_G_L_0_G"
+                            android:translateX="6"
+                            android:translateY="5">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?attr/textColor"
+                                android:fillType="nonZero"
+                                android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c " />
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="strokeAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_4_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_4_T_0_M">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleY"
+                    android:startOffset="350"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.02"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_4_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.09375"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.521,0 0.942,0.896 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_4_T_0_M">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleY"
+                    android:startOffset="367"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="500"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_to_error.xml b/core/res/res/anim/lock_to_error.xml
new file mode 100755
index 0000000..29b4964
--- /dev/null
+++ b/core/res/res/anim/lock_to_error.xml
@@ -0,0 +1,574 @@
+<!--
+     Copyright (C) 2019 The Android Open 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_3_G"
+                    android:pivotY="-32"
+                    android:scaleX="0.12762"
+                    android:scaleY="0.12762"
+                    android:translateX="12"
+                    android:translateY="39.871">
+                    <path
+                        android:name="_R_G_L_3_G_D_0_P_0"
+                        android:fillAlpha="0"
+                        android:fillColor="#ff0000"
+                        android:fillType="nonZero"
+                        android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                        android:trimPathStart="0"
+                        android:trimPathEnd="1"
+                        android:trimPathOffset="0" />
+                    <path
+                        android:name="_R_G_L_3_G_D_1_P_0"
+                        android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                        android:strokeWidth="16"
+                        android:strokeAlpha="1"
+                        android:strokeColor="?attr/textColor"
+                        android:trimPathStart="0"
+                        android:trimPathEnd="1"
+                        android:trimPathOffset="0" />
+                </group>
+                <group
+                    android:name="_R_G_L_2_G_T_1"
+                    android:rotation="45"
+                    android:scaleX="1"
+                    android:scaleY="1"
+                    android:translateX="12"
+                    android:translateY="15">
+                    <group
+                        android:name="_R_G_L_2_G"
+                        android:translateX="-2.25"
+                        android:translateY="-2.25">
+                        <path
+                            android:name="_R_G_L_2_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="?attr/textColor"
+                            android:fillType="nonZero"
+                            android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_T_1"
+                    android:scaleX="0.125"
+                    android:scaleY="0.125"
+                    android:translateX="12.023"
+                    android:translateY="1.281">
+                    <group
+                        android:name="_R_G_L_1_G"
+                        android:translateX="0.317"
+                        android:translateY="45.25">
+                        <path
+                            android:name="_R_G_L_1_G_D_0_P_0"
+                            android:fillAlpha="0"
+                            android:fillColor="#EA4335"
+                            android:fillType="nonZero"
+                            android:pathData=" M4.21 -42.01 C4.21,-42.01 4.21,-32 4.21,-32 C4.21,-32 -5.25,-32 -5.25,-32 C-5.25,-32 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:pivotY="21"
+                    android:scaleX="0.14286"
+                    android:scaleY="0.14286"
+                    android:translateX="12"
+                    android:translateY="-6">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="0"
+                        android:fillColor="#ffffff"
+                        android:fillType="nonZero"
+                        android:pathData=" M49.24 -12.32 C49.24,-12.32 49.24,52.91 49.24,52.91 C49.24,58.95 44.34,63.85 38.3,63.85 C38.3,63.85 -37.8,63.85 -37.8,63.85 C-43.84,63.85 -48.74,58.95 -48.74,52.91 C-48.74,52.91 -48.74,-12.32 -48.74,-12.32 C-48.74,-18.37 -43.84,-23.27 -37.8,-23.27 C-37.8,-23.27 38.3,-23.27 38.3,-23.27 C44.34,-23.27 49.24,-18.37 49.24,-12.32c " />
+                    <path
+                        android:name="_R_G_L_0_G_D_1_P_0"
+                        android:pathData=" M49.24 -12.32 C49.24,-12.32 49.24,52.91 49.24,52.91 C49.24,58.95 44.34,63.85 38.3,63.85 C38.3,63.85 -37.8,63.85 -37.8,63.85 C-43.84,63.85 -48.74,58.95 -48.74,52.91 C-48.74,52.91 -48.74,-12.32 -48.74,-12.32 C-48.74,-18.37 -43.84,-23.27 -37.8,-23.27 C-37.8,-23.27 38.3,-23.27 38.3,-23.27 C44.34,-23.27 49.24,-18.37 49.24,-12.32c "
+                        android:strokeWidth="14"
+                        android:strokeAlpha="1"
+                        android:strokeColor="?attr/textColor"
+                        android:strokeLineCap="round"
+                        android:strokeLineJoin="round" />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_3_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="#fff"
+                    android:valueTo="#fff"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="117"
+                    android:valueFrom="#fff"
+                    android:valueTo="#EA4335"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="strokeWidth"
+                    android:startOffset="0"
+                    android:valueFrom="16"
+                    android:valueTo="8"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathStart"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="trimPathEnd"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.5"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleY"
+                    android:startOffset="183"
+                    android:valueFrom="0.12762"
+                    android:valueTo="0"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#fff"
+                    android:valueTo="#fff"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillColor"
+                    android:startOffset="117"
+                    android:valueFrom="#fff"
+                    android:valueTo="#EA4335"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c "
+                    android:valueTo="M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="pathData"
+                    android:startOffset="133"
+                    android:valueFrom="M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c "
+                    android:valueTo="M2.25 0.25 C2.25,0.25 4.25,2.25 4.25,2.25 C4.25,2.25 2.25,4.25 2.25,4.25 C2.25,4.25 0.25,2.25 0.25,2.25 C0.25,2.25 2.25,0.25 2.25,0.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="17"
+                    android:pathData="M 12,15C 12,15.171875 12,14.828125 12,15"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:pathData="M 12,15C 12,15.171875 12,15.859124999999999 12,16.031"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="17">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="scaleX"
+                    android:startOffset="17"
+                    android:valueFrom="1"
+                    android:valueTo="0.4325"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="scaleY"
+                    android:startOffset="17"
+                    android:valueFrom="1"
+                    android:valueTo="0.4325"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="150"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M4.21 -42.01 C4.21,-42.01 4.21,-32 4.21,-32 C4.21,-32 -5.25,-32 -5.25,-32 C-5.25,-32 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueTo="M4.21 -42.01 C4.21,-42.01 4.21,-32 4.21,-32 C4.21,-32 -5.25,-32 -5.25,-32 C-5.25,-32 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="183"
+                    android:propertyName="pathData"
+                    android:startOffset="167"
+                    android:valueFrom="M4.21 -42.01 C4.21,-42.01 4.21,-32 4.21,-32 C4.21,-32 -5.25,-32 -5.25,-32 C-5.25,-32 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueTo="M4.21 -42.01 C4.21,-42.01 4.16,33 4.16,33 C4.16,33 -5.3,33 -5.3,33 C-5.3,33 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="50"
+                    android:propertyName="pathData"
+                    android:startOffset="350"
+                    android:valueFrom="M4.21 -42.01 C4.21,-42.01 4.16,33 4.16,33 C4.16,33 -5.3,33 -5.3,33 C-5.3,33 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueTo="M4.21 -42.01 C4.21,-42.01 4.19,30.81 4.19,30.81 C4.19,30.81 -5.27,30.81 -5.27,30.81 C-5.27,30.81 -5.25,-42.01 -5.25,-42.01 C-5.25,-42.01 4.21,-42.01 4.21,-42.01c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:pathData="M 12.023,1.281C 12.023,1.656 12.023,0.9059999999999999 12.023,1.281"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="183"
+                    android:pathData="M 12.023,1.281C 12.023,1.656 12.023,3.156 12.023,3.531"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="167">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleX"
+                    android:startOffset="167"
+                    android:valueFrom="0"
+                    android:valueTo="0.125"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="267"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M49.24 -12.32 C49.24,-12.32 49.24,52.91 49.24,52.91 C49.24,58.95 44.34,63.85 38.3,63.85 C38.3,63.85 -37.8,63.85 -37.8,63.85 C-43.84,63.85 -48.74,58.95 -48.74,52.91 C-48.74,52.91 -48.74,-12.32 -48.74,-12.32 C-48.74,-18.37 -43.84,-23.27 -37.8,-23.27 C-37.8,-23.27 38.3,-23.27 38.3,-23.27 C44.34,-23.27 49.24,-18.37 49.24,-12.32c "
+                    android:valueTo="M75.1 -1.1 C75.1,19.57 66.72,38.28 53.18,51.83 C39.63,65.37 20.92,73.75 0.25,73.75 C-20.42,73.75 -39.13,65.37 -52.68,51.83 C-66.22,38.28 -74.6,19.57 -74.6,-1.1 C-74.6,-21.77 -66.22,-40.48 -52.68,-54.02 C-39.13,-67.57 -20.42,-75.95 0.25,-75.95 C20.92,-75.95 39.63,-67.57 53.18,-54.02 C66.72,-40.48 75.1,-21.77 75.1,-1.1c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="117"
+                    android:propertyName="strokeColor"
+                    android:startOffset="0"
+                    android:valueFrom="#fff"
+                    android:valueTo="#fff"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="17"
+                    android:propertyName="strokeColor"
+                    android:startOffset="117"
+                    android:valueFrom="#fff"
+                    android:valueTo="#EA4335"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="267"
+                    android:propertyName="strokeWidth"
+                    android:startOffset="0"
+                    android:valueFrom="14"
+                    android:valueTo="12"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="267"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M49.24 -12.32 C49.24,-12.32 49.24,52.91 49.24,52.91 C49.24,58.95 44.34,63.85 38.3,63.85 C38.3,63.85 -37.8,63.85 -37.8,63.85 C-43.84,63.85 -48.74,58.95 -48.74,52.91 C-48.74,52.91 -48.74,-12.32 -48.74,-12.32 C-48.74,-18.37 -43.84,-23.27 -37.8,-23.27 C-37.8,-23.27 38.3,-23.27 38.3,-23.27 C44.34,-23.27 49.24,-18.37 49.24,-12.32c "
+                    android:valueTo="M75.1 -1.1 C75.1,19.57 66.72,38.28 53.18,51.83 C39.63,65.37 20.92,73.75 0.25,73.75 C-20.42,73.75 -39.13,65.37 -52.68,51.83 C-66.22,38.28 -74.6,19.57 -74.6,-1.1 C-74.6,-21.77 -66.22,-40.48 -52.68,-54.02 C-39.13,-67.57 -20.42,-75.95 0.25,-75.95 C20.92,-75.95 39.63,-67.57 53.18,-54.02 C66.72,-40.48 75.1,-21.77 75.1,-1.1c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1017"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_unlock.xml b/core/res/res/anim/lock_unlock.xml
new file mode 100755
index 0000000..9cc31a0
--- /dev/null
+++ b/core/res/res/anim/lock_unlock.xml
@@ -0,0 +1,337 @@
+<!--
+     Copyright (C) 2019 The Android Open 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_2_G_T_1"
+                    android:translateX="12"
+                    android:translateY="15">
+                    <group
+                        android:name="_R_G_L_2_G"
+                        android:translateX="-8.25"
+                        android:translateY="-7.25">
+                        <path
+                            android:name="_R_G_L_2_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="?attr/textColor"
+                            android:fillType="nonZero"
+                            android:pathData=" M14.25 0.25 C14.25,0.25 12.75,0.25 12.75,0.25 C12.75,0.25 10.75,0.25 10.75,0.25 C10.75,0.25 5.75,0.25 5.75,0.25 C5.75,0.25 3.75,0.25 3.75,0.25 C3.75,0.25 2.25,0.25 2.25,0.25 C1.15,0.25 0.25,1.15 0.25,2.25 C0.25,2.25 0.25,12.25 0.25,12.25 C0.25,13.35 1.15,14.25 2.25,14.25 C2.25,14.25 14.25,14.25 14.25,14.25 C15.35,14.25 16.25,13.35 16.25,12.25 C16.25,12.25 16.25,2.25 16.25,2.25 C16.25,1.15 15.35,0.25 14.25,0.25c  M14.25 12.25 C14.25,12.25 2.25,12.25 2.25,12.25 C2.25,12.25 2.25,2.25 2.25,2.25 C2.25,2.25 3.75,2.25 3.75,2.25 C3.75,2.25 12.75,2.25 12.75,2.25 C12.75,2.25 14.25,2.25 14.25,2.25 C14.25,2.25 14.25,12.25 14.25,12.25c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_N_5_T_1"
+                    android:translateX="12"
+                    android:translateY="15">
+                    <group
+                        android:name="_R_G_L_1_G_N_5_T_0"
+                        android:translateX="-8.25"
+                        android:translateY="-7.25">
+                        <group
+                            android:name="_R_G_L_1_G"
+                            android:pivotX="2.25"
+                            android:pivotY="2.25"
+                            android:scaleX="1"
+                            android:scaleY="1"
+                            android:translateX="6"
+                            android:translateY="5">
+                            <path
+                                android:name="_R_G_L_1_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="?attr/textColor"
+                                android:fillType="nonZero"
+                                android:pathData=" M2.25 0.25 C3.35,0.25 4.25,1.15 4.25,2.25 C4.25,3.35 3.35,4.25 2.25,4.25 C1.15,4.25 0.25,3.35 0.25,2.25 C0.25,1.15 1.15,0.25 2.25,0.25c " />
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_5_T_1"
+                    android:translateX="12"
+                    android:translateY="15">
+                    <group
+                        android:name="_R_G_L_0_G_N_5_T_0"
+                        android:translateX="-8.25"
+                        android:translateY="-7.25">
+                        <group
+                            android:name="_R_G_L_0_G"
+                            android:pivotX="27.965"
+                            android:pivotY="-32"
+                            android:scaleX="0.125"
+                            android:scaleY="0.125"
+                            android:translateX="-16.219"
+                            android:translateY="32.25">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="0"
+                                android:fillColor="#ff0000"
+                                android:fillType="nonZero"
+                                android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 " />
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:pathData=" M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                                android:strokeWidth="16"
+                                android:strokeAlpha="1"
+                                android:strokeColor="?attr/textColor"/>
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,15C 12,14.85416667163372 12,14.125 12,14.125"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,14.125C 12,14.125 12,15.05870145463943 12,15.324"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="133">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:pathData="M 12,15.324C 12,15.40252217555046 12,14.96669441461563 12,15"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="267">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.85"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="0.85"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="283"
+                    android:propertyName="scaleX"
+                    android:startOffset="100"
+                    android:valueFrom="0.85"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="283"
+                    android:propertyName="scaleY"
+                    android:startOffset="100"
+                    android:valueFrom="0.85"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_5_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,15C 12,14.85416667163372 12,14.125 12,14.125"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,14.125C 12,14.125 12,15.05870145463943 12,15.324"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="133">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:pathData="M 12,15.324C 12,15.40252217555046 12,14.96669441461563 12,15"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="267">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueTo="M-27.97 -48.55 C-27.97,-48.55 -28,-53.75 -27.97,-55.48 C-27.92,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.353,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="67"
+                    android:valueFrom="M-27.97 -48.55 C-27.97,-48.55 -28,-53.75 -27.97,-55.48 C-27.92,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueTo="M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.542,0 0.026,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-28.21 -31.92 C-28.21,-31.92 -27.85,-48.38 -27.97,-55.48 C-28,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueTo="M-27.97 -48.55 C-27.97,-48.55 -28,-53.75 -27.97,-55.48 C-27.92,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.353,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="67"
+                    android:valueFrom="M-27.97 -48.55 C-27.97,-48.55 -28,-53.75 -27.97,-55.48 C-27.92,-57.63 -23.5,-79.87 -0.75,-79.82 C22.77,-79.76 27.75,-59.37 27.72,-58.27 C27.55,-52.88 27.97,-31.67 27.97,-31.67 "
+                    android:valueTo="M79.79 -48.55 C79.79,-48.55 79.75,-53.75 79.78,-55.48 C79.83,-57.62 79.08,-78.36 53.07,-78.83 C29.5,-79.25 25.2,-59.38 25.22,-58.27 C25.25,-56.25 24.97,-31.17 24.97,-31.17 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.542,0 0.026,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_5_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,15C 12,14.85416667163372 12,14.125 12,14.125"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="0">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:pathData="M 12,14.125C 12,14.125 12,15.05870145463943 12,15.324"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="133">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="100"
+                    android:pathData="M 12,15.324C 12,15.40252217555046 12,14.96669441461563 12,15"
+                    android:propertyName="translateXY"
+                    android:propertyXName="translateX"
+                    android:propertyYName="translateY"
+                    android:startOffset="267">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="717"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/chooser_content_preview_rounded.xml b/core/res/res/drawable/chooser_content_preview_rounded.xml
new file mode 100644
index 0000000..7d85738
--- /dev/null
+++ b/core/res/res/drawable/chooser_content_preview_rounded.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <stroke
+        android:width="1dp"
+        android:color="#ccc" />
+ 
+    <corners android:radius="@dimen/chooser_corner_radius" />
+
+    <padding
+        android:left="16dp"
+        android:top="12dp"
+        android:right="16dp"
+        android:bottom="12dp"/>
+</shape>
diff --git a/core/res/res/drawable/ic_auth_error.xml b/core/res/res/drawable/ic_auth_error.xml
new file mode 100644
index 0000000..ea5f572
--- /dev/null
+++ b/core/res/res/drawable/ic_auth_error.xml
@@ -0,0 +1,23 @@
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector android:height="24dp" android:viewportHeight="60"
+        android:viewportWidth="60" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="?attr/colorError" android:pathData="M28.8,37.5l2.4,0l0,2.5l-2.4,0z"/>
+    <path android:fillColor="?attr/colorError" android:pathData="M28.8,17.5l2.4,0l0,15l-2.4,0z"/>
+    <path android:fillColor="#00000000"
+          android:pathData="M30,6.2c-13.1,0 -23.7,10.6 -23.7,23.8S16.9,53.8 30,53.8c13.1,0 23.8,-10.6 23.8,-23.8S43.1,6.2 30,6.2z"
+          android:strokeColor="?android:attr/colorError" android:strokeWidth="2.5"/>
+</vector>
diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
new file mode 100644
index 0000000..940da94
--- /dev/null
+++ b/core/res/res/drawable/ic_content_copy_gm2.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#F999"
+      android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_lock_24dp.xml b/core/res/res/drawable/ic_lock_24dp.xml
new file mode 100644
index 0000000..d848426
--- /dev/null
+++ b/core/res/res/drawable/ic_lock_24dp.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="?attr/textColor"
+        android:pathData="M12.0,15.0m-2.0,0.0a2.0,2.0 0.0,1.0 1.0,4.0 0.0a2.0,2.0 0.0,1.0 1.0,-4.0 0.0"/>
+    <path
+        android:pathData="M0,0h24v24H0V0z"
+        android:fillColor="#00000000"/>
+    <path
+        android:fillColor="?attr/textColor"
+        android:pathData="M18.0,8.0l-1.5,0.0L16.5,5.5C16.5,3.01 14.49,1.0 12.0,1.0S7.5,3.01 7.5,5.5L7.5,8.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.0zM9.5,5.5C9.5,4.12 10.62,3.0 12.0,3.0c1.38,0.0 2.5,1.12 2.5,2.5L14.5,8.0l-5.0,0.0L9.5,5.5zM18.0,20.0L6.0,20.0L6.0,10.0l1.5,0.0l9.0,0.0L18.0,10.0L18.0,20.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_lock_open_24dp.xml b/core/res/res/drawable/ic_lock_open_24dp.xml
new file mode 100644
index 0000000..94eecad
--- /dev/null
+++ b/core/res/res/drawable/ic_lock_open_24dp.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="?attr/textColor"
+        android:pathData="M12.0,15.0m-2.0,0.0a2.0,2.0 0.0,1.0 1.0,4.0 0.0a2.0,2.0 0.0,1.0 1.0,-4.0 0.0"/>
+    <path
+        android:fillColor="?attr/textColor"
+        android:pathData="M18.5,1.0C16.01,1.0 14.0,3.01 14.0,5.5L14.0,8.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.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-2.0,0.0L16.0,5.5C16.0,4.12 17.12,3.0 18.5,3.0C19.88,3.0 21.0,4.12 21.0,5.5L21.0,6.0l2.0,0.0L23.0,5.5C23.0,3.01 20.99,1.0 18.5,1.0zM18.0,10.0l0.0,10.0L6.0,20.0L6.0,10.0L18.0,10.0z"/>
+    <path
+        android:pathData="M0,0h24v24H0V0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_media_seamless.xml b/core/res/res/drawable/ic_media_seamless.xml
new file mode 100644
index 0000000..122e9c5
--- /dev/null
+++ b/core/res/res/drawable/ic_media_seamless.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,3l0.01,10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17V7h4V3H12zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/>
+</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index c42f43a..a24f920 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -17,19 +17,19 @@
 */
 -->
 <com.android.internal.widget.ResolverDrawerLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:maxWidth="@dimen/resolver_max_width"
-        android:maxCollapsedHeight="288dp"
-        android:maxCollapsedHeightSmall="56dp"
-        android:id="@id/contentPanel">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:maxWidth="@dimen/resolver_max_width"
+    android:maxCollapsedHeight="288dp"
+    android:maxCollapsedHeightSmall="56dp"
+    android:id="@id/contentPanel">
 
     <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alwaysShow="true"
-            android:background="?attr/colorBackgroundFloating" >
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alwaysShow="true"
+        android:background="?attr/colorBackgroundFloating">
         <TextView android:id="@+id/profile_button"
                   android:layout_width="wrap_content"
                   android:layout_height="48dp"
@@ -51,73 +51,98 @@
                   android:textAppearance="?attr/textAppearanceMedium"
                   android:textSize="20sp"
                   android:gravity="center"
-                  android:paddingEnd="?attr/dialogPreferredPadding"
-                  android:paddingTop="12dp"
-                  android:paddingBottom="6dp"
+                  android:paddingTop="18dp"
+                  android:paddingBottom="18dp"
+                  android:paddingLeft="24dp"
+                  android:paddingRight="24dp"
                   android:layout_below="@id/profile_button"
                   android:layout_centerHorizontal="true"/>
     </RelativeLayout>
 
-    <RelativeLayout 
+    <LinearLayout
         android:id="@+id/content_preview"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingBottom="@dimen/chooser_view_spacing"
         android:background="?attr/colorBackgroundFloating">
-      
-      <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
-            android:id="@+id/content_preview_thumbnail"
-            android:layout_alignParentTop="true"
-            android:layout_width="100dp"
+
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentStart="true"
-            android:gravity="center"
-            android:adjustViewBounds="true"
-            android:maxWidth="90dp"
-            android:maxHeight="90dp"
-            android:scaleType="fitCenter"
-            android:padding="5dp"/>
+            android:orientation="horizontal"
+            android:paddingLeft="@dimen/chooser_edge_margin_normal"
+            android:paddingRight="@dimen/chooser_edge_margin_normal"
+            android:layout_marginBottom="@dimen/chooser_view_spacing"
+            android:id="@+id/content_preview_text_layout">
+            <TextView
+                android:id="@+id/content_preview_text"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:ellipsize="end"
+                android:gravity="start|top"
+                android:paddingRight="24dp"
+                android:maxLines="2"/>
+            <Button
+                android:id="@+id/copy_button"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:gravity="center"
+                android:layout_gravity="center_vertical"
+                android:background="@drawable/ic_content_copy_gm2"/>
+        </LinearLayout>
 
-      <TextView
-          android:id="@+id/content_preview_title"
-          android:layout_alignParentTop="true"
-          android:layout_toEndOf="@id/content_preview_thumbnail"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:gravity="start|top"
-          android:textAppearance="?attr/textAppearanceMedium"
-          android:maxLines="2"
-          android:ellipsize="end"
-          android:paddingStart="15dp"
-          android:paddingEnd="15dp"
-          android:paddingTop="10dp" />
+        <!-- Required sub-layout so we can get the nice rounded corners-->
+        <!-- around this section -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
+            android:layout_marginRight="@dimen/chooser_edge_margin_thin"
+            android:minHeight="80dp"
+            android:background="@drawable/chooser_content_preview_rounded"
+            android:id="@+id/content_preview_title_layout">
 
-      <TextView
-          android:id="@+id/content_preview_text"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:layout_below="@id/content_preview_title"
-          android:layout_toEndOf="@id/content_preview_thumbnail"
-          android:gravity="start|top"
-          android:maxLines="2"
-          android:ellipsize="end"
-          android:paddingStart="15dp"
-          android:paddingEnd="15dp"
-          android:paddingTop="10dp"
-          android:paddingBottom="5dp"/>
-    </RelativeLayout>
+            <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+                  android:id="@+id/content_preview_thumbnail"
+                  android:layout_width="80dp"
+                  android:layout_height="80dp"
+                  android:layout_marginRight="12dp"
+                  android:adjustViewBounds="true"
+                  android:layout_gravity="center_vertical"
+                  android:gravity="center"
+                  android:maxWidth="70dp"
+                  android:maxHeight="70dp"
+                  android:padding="5dp"
+                  android:scaleType="centerCrop"/>
+
+            <TextView
+                android:id="@+id/content_preview_title"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:ellipsize="end"
+                android:maxLines="2"
+                android:textAppearance="?attr/textAppearanceMedium"/>
+        </LinearLayout>
+    </LinearLayout>
 
     <ListView
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:id="@+id/resolver_list"
-            android:clipToPadding="false"
-            android:scrollbarStyle="outsideOverlay"
-            android:background="?attr/colorBackgroundFloating"
-            android:elevation="8dp"
-            android:listSelector="@color/transparent"
-            android:divider="@null"
-            android:scrollIndicators="top"
-            android:nestedScrollingEnabled="true" />
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/resolver_list"
+        android:clipToPadding="false"
+        android:scrollbarStyle="outsideOverlay"
+        android:background="?attr/colorBackgroundFloating"
+        android:elevation="8dp"
+        android:listSelector="@color/transparent"
+        android:divider="@null"
+        android:scrollIndicators="top"
+        android:nestedScrollingEnabled="true"/>
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
@@ -127,6 +152,6 @@
               android:text="@string/noApplications"
               android:padding="32dp"
               android:gravity="center"
-              android:visibility="gone" />
+              android:visibility="gone"/>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 5cb93eb..56f7a59 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -79,6 +79,10 @@
                 layout="@layout/notification_material_media_action"
                 android:id="@+id/action4"
             />
+            <include
+                layout="@layout/notification_material_media_action"
+                android:id="@+id/media_seamless"
+            />
         </LinearLayout>
     </LinearLayout>
 </com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0abe456..759fc12 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -280,6 +280,9 @@
         <!-- Additional flag from base permission type: this permission designates the app
             that will approve the sharing of incident reports. -->
         <flag name="incidentReportApprover" value="0x100000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system app predictor -->
+        <flag name="appPredictor" value="0x200000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -1116,6 +1119,15 @@
          -->
     <attr name="classLoader" format="string" />
 
+    <!-- Name of the class that gets invoked for preloading application code, when starting an
+         {@link android.R.attr#isolatedProcess} service that has
+         {@link android.R.attr#useAppZygote} set to <code>true</code>. This is a fully
+         qualified class name (for example, com.mycompany.myapp.MyZygotePreload); as a
+         short-hand if the first character of the class is a period then it is appended
+         to your package name. The class must implement the {@link android.app.ZygotePreload}
+         interface. -->
+    <attr name="zygotePreloadName" format="string"/>
+
     <!-- If set to <code>true</code>, indicates to the platform that this APK is
          a 'feature' split and that it implicitly depends on the base APK. This distinguishes
          this split APK from a 'configuration' split, which provides resource overrides
@@ -1129,10 +1141,12 @@
          resource] to be present in order to function. Default value is false. -->
     <attr name="isSplitRequired" format="boolean" />
 
-    <!-- Flag to specify if this app prioritizes code integrity. The system may choose
-         to run with better integrity guarantee in various components if possible based on the app's
-         <code>targetSdkVersion</code>. -->
-    <attr name="preferCodeIntegrity" format="boolean" />
+    <!-- Flag to specify if this app wants to run the dex within its APK but not extracted or
+         locally compiled variants. This keeps the dex code protected by the APK signature. Such
+         apps will always run in JIT mode (same when they are first installed), and the system will
+         never generate ahead-of-time compiled code for them. Depending on the app's workload,
+         there may be some run time performance change, noteably the cold start time. -->
+    <attr name="useEmbeddedDex" format="boolean" />
 
     <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
          {@code <application>} tag. If specified on the {@code <application>}
@@ -1436,26 +1450,20 @@
 
     <attr name="usesNonSdkApi" format="boolean" />
 
-    <!-- Specify the type of foreground service. Apps targeting API
-     {@link android.os.Build.VERSION_CODES#Q} or later must specify foreground service type,
-     otherwise a SecurityException is thrown when
-     {@link android.app.Service#startForeground(int, Notification)} on this service is called.
-    -->
+    <!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
+         together. -->
     <attr name="foregroundServiceType">
         <!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch,
         transfer over network between device and cloud.  -->
-        <enum name="sync" value="1" />
+        <flag name="dataSync" value="0x01" />
         <!-- Music, video, news or other media play. -->
-        <enum name="mediaPlay" value="2" />
+        <flag name="mediaPlayback" value="0x02" />
         <!-- Ongoing phone call or video conference. -->
-        <enum name="phoneCall" value="3" />
+        <flag name="phoneCall" value="0x04" />
         <!-- GPS, map, navigation location update. -->
-        <enum name="location" value="4" />
+        <flag name="location" value="0x08" />
         <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. -->
-        <enum name="deviceCompanion" value="5" />
-        <!-- Process that should not be interrupted, including installation, setup, photo
-        compression etc. -->
-        <enum name="ongoingProcess" value="6" />
+        <flag name="connectedDevice" value="0x10" />
     </attr>
 
 
@@ -1602,7 +1610,7 @@
              to honor this flag as well. -->
         <attr name="usesCleartextTraffic" />
         <attr name="multiArch" />
-        <attr name="preferCodeIntegrity" />
+        <attr name="useEmbeddedDex" />
         <attr name="extractNativeLibs" />
         <attr name="defaultToDeviceProtectedStorage" format="boolean" />
         <attr name="directBootAware" />
@@ -1644,6 +1652,7 @@
         <!-- If {@code true} the user is prompted to keep the app's data on uninstall -->
         <attr name="hasFragileUserData" />
 
+	<attr name="zygotePreloadName" />
     </declare-styleable>
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
@@ -2300,17 +2309,18 @@
         <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
              will be spawned from an Application Zygote, instead of the regular Zygote.
              <p>
-             The Application Zygote will pre-initialize the application's class loader,
-             and call a static callback into the application to allow it to perform
+             The Application Zygote will first pre-initialize the application's class loader. Then,
+             if the application has defined the {@link android.R.attr#zygotePreloadName} attribute,
+             the Application Zygote will call into that class to allow it to perform
              application-specific preloads (such as loading a shared library). Therefore,
              spawning from the Application Zygote will typically reduce the service
              launch time and reduce its memory usage. The downside of using this flag
              is that you will have an additional process (the app zygote itself) that
              is taking up memory. Whether actual memory usage is improved therefore strongly
              depends on the number of isolated services that an application starts,
-             and how much memory those services save by preloading. Therefore, it is
-             recommended to measure memory usage under typical workloads to determine
-             whether it makes sense to use this flag. -->
+             and how much memory those services save by preloading and sharing memory with
+             the app zygote. Therefore, it is recommended to measure memory usage under
+             typical workloads to determine whether it makes sense to use this flag. -->
         <attr name="useAppZygote" format="boolean" />
         <!-- If this is a foreground service, specify its category. -->
         <attr name="foregroundServiceType" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 67b0652..df20f85 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -712,6 +712,9 @@
     <!-- Indicates that connected MAC randomization is supported on this device -->
     <bool translatable="false" name="config_wifi_connected_mac_randomization_supported">false</bool>
 
+    <!-- Indicates that wifi link probing is supported on this device -->
+    <bool translatable="false" name="config_wifi_link_probing_supported">false</bool>
+
     <!-- Flag indicating whether we should enable the automatic brightness.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
@@ -1022,7 +1025,8 @@
     <integer name="config_displayWhiteBalanceColorTemperatureDefault">6500</integer>
 
     <!-- The display primaries, in CIE1931 XYZ color space, for display
-         white balance to use in its calculations. -->
+         white balance to use in its calculations. The array must include a total of 12 float
+         values: 3 values per color (X, Y, Z) and 4 colors (R, G, B, W) -->
     <string-array name="config_displayWhiteBalanceDisplayPrimaries">
         <!-- Red X -->   <item>0.412315</item>
         <!-- Red Y -->   <item>0.212600</item>
@@ -1040,7 +1044,7 @@
 
     <!-- The nominal white coordinates, in CIE1931 XYZ color space, for Display White Balance to
          use in its calculations. AWB will adapt this white point to the target ambient white
-         point. -->
+         point. The array must include a total of 3 float values (X, Y, Z) -->
     <string-array name="config_displayWhiteBalanceDisplayNominalWhite">
         <!-- Nominal White X --> <item>0.950456</item>
         <!-- Nominal White Y --> <item>1.000000</item>
@@ -3778,4 +3782,105 @@
 
     <!-- Whether or not aware is enabled by default -->
     <bool name="config_awareSettingAvailable">false</bool>
+
+    <!-- Display White-Balance -->
+
+    <!-- See AmbientSensor.AmbientBrightnessSensor.
+         The ambient brightness sensor rate (in milliseconds). Must be positive. -->
+    <integer name="config_displayWhiteBalanceBrightnessSensorRate">250</integer>
+
+    <!-- See AmbientFilter.
+         How long ambient brightness changes are kept and taken into consideration
+         (in milliseconds). Must be positive. -->
+    <integer name="config_displayWhiteBalanceBrightnessFilterHorizon">10000</integer>
+
+    <!-- See AmbientFilter.WeightedMovingAverageAmbientFilter.
+         Recent changes are prioritised by integrating their duration over y = x + intercept
+         (the higher it is, the less prioritised recent changes are). Must be a non-negative
+         number, or NaN to avoid this implementation. -->
+    <item name="config_displayWhiteBalanceBrightnessFilterIntercept" format="float" type="dimen">10.0</item>
+
+    <!-- See AmbientSensor.AmbientColorTemperatureSensor.
+         The ambient color temperature sensor name. -->
+    <string name="config_displayWhiteBalanceColorTemperatureSensorName">com.google.sensor.color</string>
+
+    <!-- See AmbientSensor.AmbientColorTemperatureSensor.
+         The ambient color temperature sensor rate (in milliseconds). Must be positive. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureSensorRate">250</integer>
+
+    <!-- See AmbientFilter.
+         How long ambient color temperature changes are kept and taken into consideration
+         (in milliseconds). Must be positive. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureFilterHorizon">10000</integer>
+
+    <!-- See AmbientFilter.WeightedMovingAverageAmbientFilter.
+         Recent changes are prioritised by integrating their duration over y = x + intercept
+         (the higher it is, the less prioritised recent changes are). Must be a non-negative
+         number, or NaN to avoid this implementation. -->
+    <item name="config_displayWhiteBalanceColorTemperatureFilterIntercept" format="float"
+            type="dimen">10.0</item>
+
+    <!-- See DisplayWhiteBalanceThrottler.
+         The debounce time (in milliseconds) for increasing the screen color temperature, throttled
+         if time > lastTime + debounce. Must be non-negative. -->
+    <integer name="config_displayWhiteBalanceIncreaseDebounce">5000</integer>
+
+    <!-- See DisplayWhiteBalanceThrottler.
+         The debounce time (in milliseconds) for decreasing the screen color tempearture, throttled
+         if time < lastTime - debounce. Must be non-negative. -->
+    <integer name="config_displayWhiteBalanceDecreaseDebounce">5000</integer>
+
+    <!-- See DisplayWhiteBalanceThrottler.
+         The ambient color temperature values used to determine the threshold as the corresponding
+         value in config_displayWhiteBalance{Increase,Decrease}Threholds. Must be non-empty, the
+         same length as config_displayWhiteBalance{Increase,Decrease}Thresholds, and contain
+         non-negative, strictly increasing numbers.
+
+         For example, if:
+
+         - baseThresolds = [0, 100, 1000];
+         - increaseThresholds = [0.1, 0.15, 0.2];
+         - decreaseThresholds = [0.1, 0.05, 0.0];
+
+         Then, given the ambient color temperature INCREASED from X to Y (so X < Y):
+         - If 0 <= Y < 100, we require Y > (1 + 0.1) * X = 1.1X;
+         - If 100 <= Y < 1000, we require Y > (1 + 0.15) * X = 1.15X;
+         - If 1000 <= Y, we require Y > (1 + 0.2) * X = 1.2X.
+
+         Or, if the ambient color temperature DECREASED from X to Y (so X > Y):
+         - If 0 <= Y < 100, we require Y < (1 - 0.1) * X = 0.9X;
+         - If 100 <= Y < 1000, we require Y < (1 - 0.05) * X = 0.95X;
+         - If 1000 <= Y, we require Y < (1 - 0) * X = X.
+
+         NOTE: the numbers in this example are made up, and don't represent how actual base,
+               increase or decrease thresholds would look like. -->
+    <array name="config_displayWhiteBalanceBaseThresholds">
+        <item>0.0</item>
+    </array>
+
+    <!-- See DisplayWhiteBalanceThrottler.
+         The increase threshold values, throttled if value < value * (1 + threshold). Must be
+         non-empty, the same length as config_displayWhiteBalanceBaseThresholds, and contain
+         non-negative numbers. -->
+    <array name="config_displayWhiteBalanceIncreaseThresholds">
+        <item>0.1</item>
+    </array>
+
+    <!-- See DisplayWhiteBalanceThrottler.
+         The decrease threshold values, throttled if value > value * (1 - threshold). Must be
+         non-empty, the same length as config_displayWhiteBalanceBaseThresholds, and contain
+         non-negative numbers. -->
+    <array name="config_displayWhiteBalanceDecreaseThresholds">
+        <item>0.1</item>
+    </array>
+
+    <!-- See DisplayWhiteBalanceController.
+         The ambient brightness threshold (in lux) beneath which we fall back to a fixed ambient
+         color temperature. -->
+    <item name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" format="float" type="dimen">10.0</item>
+
+    <!-- See DisplayWhiteBalanceController.
+         The ambient color temperature (in cct) to which we fall back when the ambient brightness
+         drops beneath a certain threshold. -->
+    <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ef3834c..38367fb 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -240,7 +240,7 @@
     <dimen name="notification_header_icon_size">18dp</dimen>
 
     <!-- size (width and height) of the icon in the notification header -->
-    <dimen name="notification_header_icon_size_ambient">20dp</dimen>
+    <dimen name="notification_header_icon_size_ambient">18dp</dimen>
 
     <!-- The margin before the start of the app name in the header. -->
     <dimen name="notification_header_app_name_margin_start">3dp</dimen>
@@ -715,6 +715,9 @@
     <!-- Line spacing modifier for the message field of the harmful app dialog -->
     <item name="harmful_app_message_line_spacing_modifier" type="dimen">1.22</item>
 
-    <!-- chooser corner radius -->
-    <dimen name="chooser_corner_radius">4dp</dimen>
+    <!-- chooser (sharesheet) spacing -->
+    <dimen name="chooser_corner_radius">8dp</dimen>
+    <dimen name="chooser_view_spacing">18dp</dimen>
+    <dimen name="chooser_edge_margin_thin">16dp</dimen>
+    <dimen name="chooser_edge_margin_normal">24dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ec1bac1..e7d8102 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2936,6 +2936,8 @@
         <public name="minAspectRatio" />
         <!-- @hide @SystemApi -->
         <public name="inheritShowWhenLocked" />
+        <public name="zygotePreloadName" />
+        <public name="useEmbeddedDex" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6adb4a7..fadb28f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2645,6 +2645,9 @@
     <!-- Displayed to the user to confirm that they have copied text from a web page to the clipboard. -->
     <string name="text_copied">Text copied to clipboard.</string>
 
+    <!-- Displayed to the user to confirm that they have copied text/images to the clipboard [CHAR LIMIT=NONE] -->
+    <string name="copied">Copied</string>
+
     <!-- Menu item displayed at the end of a menu to allow users to see another page worth of menu items. This is shown on any app's menu as long as the app has too many items in the menu.-->
     <string name="more_item_label">More</string>
     <!-- Prepended to the shortcut for a menu item to indicate that the user should hold the MENU button together with the shortcut to invoke the item. For example, if the shortcut to open a new tab in browser is MENU and B together, then this would be prepended to the letter "B" -->
@@ -3692,6 +3695,9 @@
     <!-- Notification action to browse external media [CHAR LIMIT=20] -->
     <string name="ext_media_browse_action">Explore</string>
 
+    <!-- Notification action to transfer media [CHAR LIMIT=40] -->
+    <string name="ext_media_seamless_action">Seamless transfer</string>
+
     <!-- Notification title when external media is missing [CHAR LIMIT=30] -->
     <string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string>
     <!-- Notification body when external media is missing [CHAR LIMIT=30] -->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 79afe69..0dc54e0 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -374,7 +374,9 @@
     <style name="TextAppearance.DeviceDefault.Caption" parent="TextAppearance.Material.Caption">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.ListItem" parent="TextAppearance.DeviceDefault.Subhead"/>
+    <style name="TextAppearance.DeviceDefault.ListItem" parent="TextAppearance.DeviceDefault.Subhead">
+        <item name="fontFamily">@string/config_headlineFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.ListItemSecondary" parent="TextAppearance.DeviceDefault.Body1"/>
 
     <!-- Preference Styles -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 54a3243..c909873 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -53,7 +53,10 @@
   <java-symbol type="id" name="content_preview" />
   <java-symbol type="id" name="content_preview_thumbnail" />
   <java-symbol type="id" name="content_preview_text" />
+  <java-symbol type="id" name="content_preview_text_layout" />
   <java-symbol type="id" name="content_preview_title" />
+  <java-symbol type="id" name="content_preview_title_layout" />
+  <java-symbol type="id" name="copy_button" />
   <java-symbol type="id" name="current_scene" />
   <java-symbol type="id" name="scene_layoutid_cache" />
   <java-symbol type="id" name="customPanel" />
@@ -191,6 +194,7 @@
   <java-symbol type="id" name="action2" />
   <java-symbol type="id" name="action3" />
   <java-symbol type="id" name="action4" />
+  <java-symbol type="id" name="media_seamless" />
   <java-symbol type="id" name="big_picture" />
   <java-symbol type="id" name="big_text" />
   <java-symbol type="id" name="chronometer" />
@@ -544,6 +548,7 @@
   <java-symbol type="string" name="activity_resolver_work_profiles_support" />
   <java-symbol type="string" name="app_running_notification_title" />
   <java-symbol type="string" name="app_running_notification_text" />
+  <java-symbol type="string" name="copied" />
   <java-symbol type="string" name="delete" />
   <java-symbol type="string" name="deleteText" />
   <java-symbol type="string" name="grant_permissions_header_text" />
@@ -1353,6 +1358,9 @@
   <java-symbol type="drawable" name="ic_text_dot" />
   <java-symbol type="drawable" name="ic_print" />
   <java-symbol type="drawable" name="ic_print_error" />
+  <java-symbol type="drawable" name="ic_lock_24dp" />
+  <java-symbol type="drawable" name="ic_lock_open_24dp" />
+  <java-symbol type="drawable" name="ic_auth_error" />
   <java-symbol type="drawable" name="jog_dial_arrow_long_left_green" />
   <java-symbol type="drawable" name="jog_dial_arrow_long_right_red" />
   <java-symbol type="drawable" name="jog_dial_arrow_short_left_and_right" />
@@ -1910,6 +1918,7 @@
   <java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" />
   <java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
   <java-symbol type="bool" name="config_wifi_connected_mac_randomization_supported" />
+  <java-symbol type="bool" name="config_wifi_link_probing_supported" />
   <java-symbol type="bool" name="config_wifi_fast_bss_transition_enabled" />
   <java-symbol type="bool" name="config_wimaxEnabled" />
   <java-symbol type="bool" name="show_ongoing_ime_switcher" />
@@ -2213,6 +2222,10 @@
   <java-symbol type="anim" name="lock_screen_wallpaper_exit" />
   <java-symbol type="anim" name="launch_task_behind_source" />
   <java-symbol type="anim" name="wallpaper_open_exit" />
+  <java-symbol type="anim" name="lock_to_error" />
+  <java-symbol type="anim" name="lock_lock" />
+  <java-symbol type="anim" name="lock_unlock" />
+  <java-symbol type="anim" name="lock_in" />
 
   <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
   <java-symbol type="dimen" name="status_bar_icon_size" />
@@ -2236,6 +2249,7 @@
   <java-symbol type="string" name="ext_media_init_action" />
   <java-symbol type="string" name="ext_media_unmount_action" />
   <java-symbol type="string" name="ext_media_browse_action" />
+  <java-symbol type="string" name="ext_media_seamless_action" />
   <java-symbol type="string" name="ext_media_missing_title" />
   <java-symbol type="string" name="ext_media_missing_message" />
   <java-symbol type="string" name="ext_media_move_specific_title" />
@@ -2708,6 +2722,9 @@
   <java-symbol type="id" name="month_view" />
   <java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
   <java-symbol type="dimen" name="chooser_corner_radius" />
+  <java-symbol type="dimen" name="chooser_view_spacing" />
+  <java-symbol type="dimen" name="chooser_edge_margin_thin" />
+  <java-symbol type="dimen" name="chooser_edge_margin_normal" />
   <java-symbol type="layout" name="chooser_grid" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="day_picker_view_pager" />
@@ -3097,6 +3114,7 @@
   <java-symbol type="drawable" name="ic_restart" />
   <java-symbol type="drawable" name="ic_screenshot" />
   <java-symbol type="drawable" name="ic_faster_emergency" />
+  <java-symbol type="drawable" name="ic_media_seamless" />
   <java-symbol type="drawable" name="emergency_icon" />
 
   <java-symbol type="array" name="config_convert_to_emergency_number_map" />
@@ -3587,4 +3605,20 @@
   <java-symbol type="integer" name="config_attentionApiTimeout" />
 
   <java-symbol type="string" name="config_incidentReportApproverPackage" />
+
+  <!-- Display White-Balance -->
+  <java-symbol type="integer" name="config_displayWhiteBalanceBrightnessSensorRate" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceBrightnessFilterHorizon" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceBrightnessFilterIntercept" />
+  <java-symbol type="string" name="config_displayWhiteBalanceColorTemperatureSensorName" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureSensorRate" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureFilterHorizon" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceColorTemperatureFilterIntercept" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceIncreaseDebounce" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceDecreaseDebounce" />
+  <java-symbol type="array" name="config_displayWhiteBalanceBaseThresholds" />
+  <java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 268bb81..1764249 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -59,6 +59,7 @@
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
+    <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_SMS"/>
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index bdf3aa2..a5ea441 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -465,12 +465,12 @@
 
         @Override
         public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo,
-                CompatibilityInfo compatibilityInfo, int i) throws RemoteException {
+                CompatibilityInfo compatibilityInfo, int i, int userId) throws RemoteException {
         }
 
         @Override
         public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo,
-                CompatibilityInfo compatibilityInfo) throws RemoteException {
+                CompatibilityInfo compatibilityInfo, int userId) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index e79d2ae..d2a1dd9 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.hardware.HardwareBuffer;
+
 import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
@@ -233,14 +235,31 @@
     }
 
     @SmallTest
-    public void testCreateHardwareBitmapFromGraphicBuffer() {
+    public void testWrapHardwareBufferWithSrgbColorSpace() {
         GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
                 GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
         Canvas canvas = buffer.lockCanvas();
         canvas.drawColor(Color.YELLOW);
         buffer.unlockCanvasAndPost(canvas);
-        Bitmap hardwareBitmap = Bitmap.createHardwareBitmap(buffer);
+        Bitmap hardwareBitmap =
+                Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer), null);
         assertTrue(hardwareBitmap.isPremultiplied());
         assertFalse(hardwareBitmap.isMutable());
+        assertEquals(ColorSpace.get(ColorSpace.Named.SRGB), hardwareBitmap.getColorSpace());
+    }
+
+    @SmallTest
+    public void testWrapHardwareBufferWithDisplayP3ColorSpace() {
+        GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
+                GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
+        Canvas canvas = buffer.lockCanvas();
+        canvas.drawColor(Color.YELLOW);
+        buffer.unlockCanvasAndPost(canvas);
+        Bitmap hardwareBitmap = Bitmap.wrapHardwareBuffer(
+                HardwareBuffer.createFromGraphicBuffer(buffer),
+                ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+        assertTrue(hardwareBitmap.isPremultiplied());
+        assertFalse(hardwareBitmap.isMutable());
+        assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), hardwareBitmap.getColorSpace());
     }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e32b412..2cb925a 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -137,6 +137,7 @@
                     Settings.Global.BROADCAST_OFFLOAD_CONSTANTS,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
+                    Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
                     Settings.Global.BATTERY_STATS_CONSTANTS,
                     Settings.Global.BINDER_CALLS_STATS,
@@ -222,6 +223,7 @@
                     Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
                     Settings.Global.DEVICE_DEMO_MODE,
                     Settings.Global.DEVICE_IDLE_CONSTANTS,
+                    Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
                     Settings.Global.BATTERY_SAVER_CONSTANTS,
                     Settings.Global.BATTERY_TIP_CONSTANTS,
                     Settings.Global.DEFAULT_SM_DP_PLUS,
@@ -295,6 +297,7 @@
                     Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
                     Settings.Global.JOB_SCHEDULER_CONSTANTS,
                     Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
+                    Settings.Global.KERNEL_CPU_THREAD_READER,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
                     Settings.Global.LANG_ID_UPDATE_METADATA_URL,
                     Settings.Global.LAST_ACTIVE_USER_ID,
@@ -525,6 +528,7 @@
                     Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
                     Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
                     Settings.Global.WIFI_PNO_RECENCY_SORTING_ENABLED,
+                    Settings.Global.WIFI_LINK_PROBING_ENABLED,
                     Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
                     Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                     Settings.Global.WIFI_NETWORK_SHOW_RSSI,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 5e58f82..582be9d 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -21,8 +21,10 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -246,25 +248,31 @@
     public void testClassifyText_foreignText() {
         LocaleList originalLocales = LocaleList.getDefault();
         LocaleList.setDefault(LocaleList.forLanguageTags("en"));
-        String foreignText = "これは日本語のテキストです";
+        String japaneseText = "これは日本語のテキストです";
 
         Context context = new FakeContextBuilder()
                 .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
                 .build();
         TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
         TextClassification.Request request = new TextClassification.Request.Builder(
-                foreignText, 0, foreignText.length())
+                japaneseText, 0, japaneseText.length())
                 .setDefaultLocales(LOCALES)
                 .build();
 
         TextClassification classification = classifier.classifyText(request);
+        RemoteAction translateAction = classification.getActions().get(0);
         assertEquals(1, classification.getActions().size());
         assertEquals(
                 context.getString(com.android.internal.R.string.translate),
-                classification.getActions().get(0).getTitle());
-        Intent intent = (Intent) classification.getExtras()
-                .getParcelableArrayList(TextClassifierImpl.ACTIONS_INTENTS).get(0);
+                translateAction.getTitle());
+
+        assertEquals(translateAction, ExtrasUtils.findTranslateAction(classification));
+        Intent intent = ExtrasUtils.getActionsIntents(classification).get(0);
         assertEquals(Intent.ACTION_TRANSLATE, intent.getAction());
+        Bundle foreignLanguageInfo = ExtrasUtils.getForeignLanguageExtra(classification);
+        assertEquals("ja", ExtrasUtils.getEntityType(foreignLanguageInfo));
+        assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) >= 0);
+        assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) <= 1);
 
         LocaleList.setDefault(originalLocales);
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 21fcae7..6b9e69c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,6 +34,9 @@
 
 import android.app.usage.UsageStatsManager;
 import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipboardManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
@@ -392,6 +395,32 @@
         assertThat(chosen[0], is(toChoose));
     }
 
+    @Test
+    public void copyTextToClipboard() throws Exception {
+        Intent sendIntent = createSendTextIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(1);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        onView(withId(R.id.copy_button)).perform(click());
+
+        ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
+                Context.CLIPBOARD_SERVICE);
+        ClipData clipData = clipboard.getPrimaryClip();
+        assertThat("testing intent sending", is(clipData.getItemAt(0).getText()));
+
+        ClipDescription clipDescription = clipData.getDescription();
+        assertThat("text/plain", is(clipDescription.getMimeType(0)));
+    }
+
     private Intent createSendTextIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index c03d1f3..3ddd8aa 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Process;
 import android.os.SystemClock;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -121,7 +122,8 @@
         workFinishedLatch.await();
 
         // Get thread data from KernelCpuThreadReader
-        final KernelCpuThreadReader kernelCpuThreadReader = KernelCpuThreadReader.create();
+        final KernelCpuThreadReader kernelCpuThreadReader =
+                KernelCpuThreadReader.create(8, uid -> uid == Process.myUid());
         assertNotNull(kernelCpuThreadReader);
         final ProcessCpuUsage currentProcessCpuUsage =
                 kernelCpuThreadReader.getCurrentProcessCpuUsage();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index 0c56b8a..b9744f5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -18,8 +18,10 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
 
 import android.content.Context;
 import android.os.FileUtils;
@@ -99,6 +101,8 @@
                 THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
 
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                8,
+                uid -> 1000 <= uid && uid < 2000,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
                 processUtils);
@@ -138,11 +142,13 @@
                     new int[][]{{uid}});
         }
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                8,
+                uidPredicate,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
                 processUtils);
         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
-                kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate);
+                kernelCpuThreadReader.getProcessCpuUsageByUids();
         processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
 
         assertEquals(expectedUids.length, processCpuUsageByUids.size());
@@ -350,4 +356,106 @@
                 4, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex(
                         new long[]{1, 2, 3, 4}));
     }
+
+    @Test
+    public void testUidPredicate_singleRange() {
+        KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate =
+                KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000-1999");
+        assertTrue(uidPredicate.test(1000));
+        assertTrue(uidPredicate.test(1050));
+        assertTrue(uidPredicate.test(1999));
+        assertFalse(uidPredicate.test(2000));
+        assertFalse(uidPredicate.test(0));
+        assertFalse(uidPredicate.test(10000));
+        assertFalse(uidPredicate.test(-100));
+    }
+
+    @Test
+    public void testUidPredicate_singleUid() {
+        KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate =
+                KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1234-1234");
+        assertTrue(uidPredicate.test(1234));
+        assertFalse(uidPredicate.test(1235));
+        assertFalse(uidPredicate.test(1232));
+        assertFalse(uidPredicate.test(0));
+        assertFalse(uidPredicate.test(-1234));
+    }
+
+    @Test
+    public void testUidPredicate_uidAndRange() {
+        KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate =
+                KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString(
+                        "1000-1000;1050-1060");
+        assertTrue(uidPredicate.test(1000));
+        assertTrue(uidPredicate.test(1050));
+        assertTrue(uidPredicate.test(1054));
+        assertTrue(uidPredicate.test(1060));
+        assertFalse(uidPredicate.test(1040));
+        assertFalse(uidPredicate.test(1001));
+        assertFalse(uidPredicate.test(0));
+        assertFalse(uidPredicate.test(-1000));
+    }
+
+    @Test
+    public void testUidPredicate_multiple() {
+        KernelCpuThreadReaderSettingsObserver.UidPredicate uidPredicate =
+                KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString(
+                        "1000-1000;1050-1060;1001-1001;2000-3000");
+        assertTrue(uidPredicate.test(1000));
+        assertTrue(uidPredicate.test(1001));
+        assertTrue(uidPredicate.test(1050));
+        assertTrue(uidPredicate.test(1054));
+        assertTrue(uidPredicate.test(1060));
+        assertTrue(uidPredicate.test(1001));
+        assertTrue(uidPredicate.test(2000));
+        assertTrue(uidPredicate.test(2444));
+        assertTrue(uidPredicate.test(3000));
+        assertFalse(uidPredicate.test(0));
+        assertFalse(uidPredicate.test(1040));
+        assertFalse(uidPredicate.test(3001));
+        assertFalse(uidPredicate.test(1999));
+    }
+
+    @Test
+    public void testUidPredicate_emptyRangeString() {
+        assertThrows(
+                NumberFormatException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString(""));
+    }
+
+    @Test
+    public void testUidPredicate_singleNumber() {
+        assertThrows(
+                NumberFormatException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1000"));
+    }
+
+    @Test
+    public void testUidPredicate_lettersInRange() {
+        assertThrows(
+                NumberFormatException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString(
+                        "0-0;1-1;a;3-3"));
+    }
+
+    @Test
+    public void testUidPredicate_onlyLetters() {
+        assertThrows(
+                NumberFormatException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("abc"));
+    }
+
+    @Test
+    public void testUidPredicate_backwardsRange() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("20-10"));
+    }
+
+    @Test
+    public void testUidPredicate_comma() {
+        assertThrows(
+                NumberFormatException.class,
+                () -> KernelCpuThreadReaderSettingsObserver.UidPredicate.fromString("1-1,2-2,3-3"));
+    }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a47ab87..bcac5fd 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -329,6 +329,7 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.STATUS_BAR_SERVICE"/>
         <permission name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"/>
     </privapp-permissions>
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 97cb2ec..080c5d5 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,7 +21,6 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.content.res.ResourcesImpl;
@@ -713,18 +712,6 @@
     }
 
     /**
-     * Create hardware bitmap backed GraphicBuffer.
-     *
-     * @return Bitmap or null if this GraphicBuffer has unsupported PixelFormat.
-     *         currently PIXEL_FORMAT_RGBA_8888 is the only supported format
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static Bitmap createHardwareBitmap(@NonNull GraphicBuffer graphicBuffer) {
-        return nativeCreateHardwareBitmap(graphicBuffer);
-    }
-
-    /**
      * Create a hardware bitmap backed by a {@link HardwareBuffer}.
      *
      * <p>The passed HardwareBuffer's usage flags must contain
@@ -1762,9 +1749,7 @@
      *         previously assigned color space.
      *
      * @param colorSpace to assign to the bitmap
-     * @hide
      */
-    @TestApi
     public void setColorSpace(@NonNull ColorSpace colorSpace) {
         checkRecycled("setColorSpace called on a recycled bitmap");
         if (colorSpace == null) {
@@ -1815,9 +1800,7 @@
      * @throws IllegalArgumentException if the color space encoded in the long
      *                                  is invalid or unknown.
      *
-     * @hide pending API approval
      */
-    @TestApi
     public void eraseColor(@ColorLong long c) {
         checkRecycled("Can't erase a recycled bitmap");
         if (!isMutable()) {
@@ -1864,9 +1847,7 @@
      * @throws IllegalArgumentException if x, y exceed the bitmap's bounds
      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
      *
-     * @hide pending API approval
      */
-    @TestApi
     public Color getColor(int x, int y) {
         checkRecycled("Can't call getColor() on a recycled bitmap");
         checkHardware("unable to getColor(), "
@@ -2256,7 +2237,6 @@
     private static native void nativePrepareToDraw(long nativeBitmap);
     private static native int nativeGetAllocationByteCount(long nativeBitmap);
     private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
-    private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer);
     private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
                                                                 long nativeColorSpace);
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index bcf7229..eb0f2e1 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -65,16 +65,6 @@
         return nativeCreate(nativeMatrix, mBitmap, mTileX, mTileY);
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected Shader copy() {
-        final BitmapShader copy = new BitmapShader(mBitmap, mTileX, mTileY);
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
     private static native long nativeCreate(long nativeMatrix, Bitmap bitmap,
             int shaderTileModeX, int shaderTileModeY);
 }
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 70a5f53..189e174 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -87,17 +87,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected Shader copy() {
-        final ComposeShader copy = new ComposeShader(
-                mShaderA.copy(), mShaderB.copy(), mPorterDuffMode);
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
     private static native long nativeCreate(long nativeMatrix,
             long nativeShaderA, long nativeShaderB, int porterDuffMode);
 }
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 7e6fc35..12e63c0 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -17,21 +17,13 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 
+
 public class LinearGradient extends Shader {
-
-    private static final int TYPE_COLORS_AND_POSITIONS = 1;
-    private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
-
-    /**
-     * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
-     * TYPE_COLOR_START_AND_COLOR_END.
-     */
-    private int mType;
-
     @UnsupportedAppUsage
     private float mX0;
     @UnsupportedAppUsage
@@ -41,16 +33,43 @@
     @UnsupportedAppUsage
     private float mY1;
     @UnsupportedAppUsage
-    private int[] mColors;
-    @UnsupportedAppUsage
     private float[] mPositions;
     @UnsupportedAppUsage
+    private TileMode mTileMode;
+
+    // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+    @UnsupportedAppUsage
+    @ColorInt
+    private int[] mColors;
+    @UnsupportedAppUsage
+    @ColorInt
     private int mColor0;
     @UnsupportedAppUsage
+    @ColorInt
     private int mColor1;
 
-    @UnsupportedAppUsage
-    private TileMode mTileMode;
+    @ColorLong
+    private final long[] mColorLongs;
+
+
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0           The x-coordinate for the start of the gradient line
+     * @param y0           The y-coordinate for the start of the gradient line
+     * @param x1           The x-coordinate for the end of the gradient line
+     * @param y1           The y-coordinate for the end of the gradient line
+     * @param colors       The sRGB colors to be distributed along the gradient line
+     * @param positions    May be null. The relative positions [0..1] of
+     *                     each corresponding color in the colors array. If this is null,
+     *                     the the colors are distributed evenly along the gradient line.
+     * @param tile         The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int[] colors,
+            @Nullable float[] positions, @NonNull TileMode tile) {
+        this(x0, y0, x1, y1, convertColors(colors), positions, tile,
+                ColorSpace.get(ColorSpace.Named.SRGB));
+    }
 
     /**
      * Create a shader that draws a linear gradient along a line.
@@ -64,21 +83,33 @@
      *                     each corresponding color in the colors array. If this is null,
      *                     the the colors are distributed evenly along the gradient line.
      * @param tile         The Shader tiling mode
-    */
-    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
-            @Nullable float positions[], @NonNull TileMode tile) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
+     *
+     * @throws IllegalArgumentException if there are less than two colors, the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one, or {@code positions}
+     *      is not {@code null} and has a different length from {@code colors}.
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorLong long[] colors,
+            @Nullable float[] positions, @NonNull TileMode tile) {
+        this(x0, y0, x1, y1, colors.clone(), positions, tile, detectColorSpace(colors));
+    }
+
+    /**
+     * Base constructor. Assumes @param colors is a copy that this object can hold onto,
+     * and all colors share @param colorSpace.
+     */
+    private LinearGradient(float x0, float y0, float x1, float y1,
+            @NonNull @ColorLong long[] colors, @Nullable float[] positions, @NonNull TileMode tile,
+            @NonNull ColorSpace colorSpace) {
+        super(colorSpace);
+
         if (positions != null && colors.length != positions.length) {
             throw new IllegalArgumentException("color and position arrays must be of equal length");
         }
-        mType = TYPE_COLORS_AND_POSITIONS;
         mX0 = x0;
         mY0 = y0;
         mX1 = x1;
         mY1 = y1;
-        mColors = colors.clone();
+        mColorLongs = colors;
         mPositions = positions != null ? positions.clone() : null;
         mTileMode = tile;
     }
@@ -90,54 +121,43 @@
      * @param y0       The y-coordinate for the start of the gradient line
      * @param x1       The x-coordinate for the end of the gradient line
      * @param y1       The y-coordinate for the end of the gradient line
+     * @param color0   The sRGB color at the start of the gradient line.
+     * @param color1   The sRGB color at the end of the gradient line.
+     * @param tile     The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1,
+            @ColorInt int color0, @ColorInt int color1,
+            @NonNull TileMode tile) {
+        this(x0, y0, x1, y1, Color.pack(color0), Color.pack(color1), tile);
+    }
+
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0       The x-coordinate for the start of the gradient line
+     * @param y0       The y-coordinate for the start of the gradient line
+     * @param x1       The x-coordinate for the end of the gradient line
+     * @param y1       The y-coordinate for the end of the gradient line
      * @param color0   The color at the start of the gradient line.
      * @param color1   The color at the end of the gradient line.
      * @param tile     The Shader tiling mode
-    */
+     *
+     * @throws IllegalArgumentException if the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one.
+     */
     public LinearGradient(float x0, float y0, float x1, float y1,
-            @ColorInt int color0, @ColorInt int color1,
+            @ColorLong long color0, @ColorLong long color1,
             @NonNull TileMode tile) {
-        mType = TYPE_COLOR_START_AND_COLOR_END;
-        mX0 = x0;
-        mY0 = y0;
-        mX1 = x1;
-        mY1 = y1;
-        mColor0 = color0;
-        mColor1 = color1;
-        mColors = null;
-        mPositions = null;
-        mTileMode = tile;
+        this(x0, y0, x1, y1, new long[] {color0, color1}, null, tile);
     }
 
     @Override
     long createNativeInstance(long nativeMatrix) {
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            return nativeCreate1(nativeMatrix, mX0, mY0, mX1, mY1,
-                    mColors, mPositions, mTileMode.nativeInt);
-        } else { // TYPE_COLOR_START_AND_COLOR_END
-            return nativeCreate2(nativeMatrix, mX0, mY0, mX1, mY1,
-                    mColor0, mColor1, mTileMode.nativeInt);
-        }
+        return nativeCreate(nativeMatrix, mX0, mY0, mX1, mY1,
+                mColorLongs, mPositions, mTileMode.nativeInt,
+                colorSpace().getNativeInstance());
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected Shader copy() {
-        final LinearGradient copy;
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            copy = new LinearGradient(mX0, mY0, mX1, mY1, mColors.clone(),
-                    mPositions != null ? mPositions.clone() : null, mTileMode);
-        } else { // TYPE_COLOR_START_AND_COLOR_END
-            copy = new LinearGradient(mX0, mY0, mX1, mY1, mColor0, mColor1, mTileMode);
-        }
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
-    private native long nativeCreate1(long matrix, float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode);
-    private native long nativeCreate2(long matrix, float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode);
+    private native long nativeCreate(long matrix, float x0, float y0, float x1, float y1,
+            long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 7eee6f4..73442db 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.Size;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
@@ -974,10 +973,7 @@
      * @see Color for APIs that help manipulate a color long.
      *
      * @return the paint's color (and alpha).
-     *
-     * @hide pending API approval
      */
-    @TestApi
     @ColorLong
     public long getColorLong() {
         return mColor;
@@ -1006,10 +1002,7 @@
      *      to set in the paint.
      * @throws IllegalArgumentException if the color space encoded in the long
      *      is invalid or unknown.
-     *
-     * @hide pending API approval
      */
-    @TestApi
     public void setColor(@ColorLong long color) {
         ColorSpace cs = Color.colorSpace(color);
         float r = Color.red(color);
@@ -1445,10 +1438,7 @@
      *
      * @throws IllegalArgumentException if the color space encoded in the long
      *      is invalid or unknown.
-     *
-     * @hide pending API approval
      */
-    @TestApi
     public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
         ColorSpace cs = Color.colorSpace(shadowColor);
         float r = Color.red(shadowColor);
@@ -1517,9 +1507,7 @@
      * Returns the color of the shadow layer.
      * @see #setShadowLayer(float,float,float,int)
      * @see #setShadowLayer(float,float,float,long)
-     * @hide pending API approval
      */
-    @TestApi
     public @ColorLong long getShadowLayerColorLong() {
         return mShadowLayerColor;
     }
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 96d6eee..dde757b 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -90,6 +90,9 @@
     public static final int RGBA_F16     = 0x16;
     public static final int RGBA_1010102 = 0x2B;
 
+    /** @hide */
+    public static final int HSV_888 = 0x37;
+
     /**
      * @deprecated use {@link android.graphics.ImageFormat#JPEG
      * ImageFormat.JPEG} instead.
@@ -109,6 +112,7 @@
                 info.bytesPerPixel = 4;
                 break;
             case RGB_888:
+            case HSV_888:
                 info.bitsPerPixel = 24;
                 info.bytesPerPixel = 3;
                 break;
@@ -227,6 +231,8 @@
                 return "RGBA_F16";
             case RGBA_1010102:
                 return "RGBA_1010102";
+            case HSV_888:
+                return "HSV_888";
             case JPEG:
                 return "JPEG";
             default:
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 41d2628..acbe3da 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -16,22 +16,13 @@
 
 package android.graphics;
 
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.ColorInt;
 import android.annotation.UnsupportedAppUsage;
 
 public class RadialGradient extends Shader {
-
-    private static final int TYPE_COLORS_AND_POSITIONS = 1;
-    private static final int TYPE_COLOR_CENTER_AND_COLOR_EDGE = 2;
-
-    /**
-     * Type of the RadialGradient: can be either TYPE_COLORS_AND_POSITIONS or
-     * TYPE_COLOR_CENTER_AND_COLOR_EDGE.
-     */
-    private int mType;
-
     @UnsupportedAppUsage
     private float mX;
     @UnsupportedAppUsage
@@ -39,16 +30,43 @@
     @UnsupportedAppUsage
     private float mRadius;
     @UnsupportedAppUsage
-    private int[] mColors;
-    @UnsupportedAppUsage
     private float[] mPositions;
     @UnsupportedAppUsage
+    private TileMode mTileMode;
+
+    // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+    @UnsupportedAppUsage
+    @ColorInt
+    private int[] mColors;
+    @UnsupportedAppUsage
+    @ColorInt
     private int mCenterColor;
     @UnsupportedAppUsage
+    @ColorInt
     private int mEdgeColor;
 
-    @UnsupportedAppUsage
-    private TileMode mTileMode;
+    @ColorLong
+    private final long[] mColorLongs;
+
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param centerX  The x-coordinate of the center of the radius
+     * @param centerY  The y-coordinate of the center of the radius
+     * @param radius   Must be positive. The radius of the circle for this gradient.
+     * @param colors   The sRGB colors to be distributed between the center and edge of the circle
+     * @param stops    May be <code>null</code>. Valid values are between <code>0.0f</code> and
+     *                 <code>1.0f</code>. The relative position of each corresponding color in
+     *                 the colors array. If <code>null</code>, colors are distributed evenly
+     *                 between the center and edge of the circle.
+     * @param tileMode The Shader tiling mode
+     */
+    public RadialGradient(float centerX, float centerY, float radius,
+            @NonNull @ColorInt int[] colors, @Nullable float[] stops,
+            @NonNull TileMode tileMode) {
+        this(centerX, centerY, radius, convertColors(colors), stops, tileMode,
+                ColorSpace.get(ColorSpace.Named.SRGB));
+    }
 
     /**
      * Create a shader that draws a radial gradient given the center and radius.
@@ -62,24 +80,36 @@
      *                 the colors array. If <code>null</code>, colors are distributed evenly
      *                 between the center and edge of the circle.
      * @param tileMode The Shader tiling mode
+     *
+     * @throws IllegalArgumentException if there are less than two colors, the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one, or {@code stops}
+     *      is not {@code null} and has a different length from {@code colors}.
      */
     public RadialGradient(float centerX, float centerY, float radius,
-            @NonNull @ColorInt int colors[], @Nullable float stops[],
+            @NonNull @ColorLong long[] colors, @Nullable float[] stops,
             @NonNull TileMode tileMode) {
+        this(centerX, centerY, radius, colors.clone(), stops, tileMode, detectColorSpace(colors));
+    }
+
+    /**
+     * Base constructor. Assumes @param colors is a copy that this object can hold onto,
+     * and all colors share @param colorSpace.
+     */
+    private RadialGradient(float centerX, float centerY, float radius,
+            @NonNull @ColorLong long[] colors, @Nullable float[] stops,
+            @NonNull TileMode tileMode, ColorSpace colorSpace) {
+        super(colorSpace);
+
         if (radius <= 0) {
             throw new IllegalArgumentException("radius must be > 0");
         }
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
         if (stops != null && colors.length != stops.length) {
             throw new IllegalArgumentException("color and position arrays must be of equal length");
         }
-        mType = TYPE_COLORS_AND_POSITIONS;
         mX = centerX;
         mY = centerY;
         mRadius = radius;
-        mColors = colors.clone();
+        mColorLongs = colors;
         mPositions = stops != null ? stops.clone() : null;
         mTileMode = tileMode;
     }
@@ -90,54 +120,41 @@
      * @param centerX     The x-coordinate of the center of the radius
      * @param centerY     The y-coordinate of the center of the radius
      * @param radius      Must be positive. The radius of the circle for this gradient
+     * @param centerColor The sRGB color at the center of the circle.
+     * @param edgeColor   The sRGB color at the edge of the circle.
+     * @param tileMode    The Shader tiling mode
+     */
+    public RadialGradient(float centerX, float centerY, float radius,
+            @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
+        this(centerX, centerY, radius, Color.pack(centerColor), Color.pack(edgeColor), tileMode);
+    }
+
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param centerX     The x-coordinate of the center of the radius
+     * @param centerY     The y-coordinate of the center of the radius
+     * @param radius      Must be positive. The radius of the circle for this gradient
      * @param centerColor The color at the center of the circle.
      * @param edgeColor   The color at the edge of the circle.
      * @param tileMode    The Shader tiling mode
+     *
+     * @throws IllegalArgumentException if the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one.
      */
     public RadialGradient(float centerX, float centerY, float radius,
-            @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
-        if (radius <= 0) {
-            throw new IllegalArgumentException("radius must be > 0");
-        }
-        mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE;
-        mX = centerX;
-        mY = centerY;
-        mRadius = radius;
-        mCenterColor = centerColor;
-        mEdgeColor = edgeColor;
-        mTileMode = tileMode;
+            @ColorLong long centerColor, @ColorLong long edgeColor, @NonNull TileMode tileMode) {
+        this(centerX, centerY, radius, new long[] {centerColor, edgeColor}, null, tileMode);
     }
 
     @Override
     long createNativeInstance(long nativeMatrix) {
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            return nativeCreate1(nativeMatrix, mX, mY, mRadius,
-                    mColors, mPositions, mTileMode.nativeInt);
-        } else { // TYPE_COLOR_CENTER_AND_COLOR_EDGE
-            return nativeCreate2(nativeMatrix, mX, mY, mRadius,
-                    mCenterColor, mEdgeColor, mTileMode.nativeInt);
-        }
+        return nativeCreate(nativeMatrix, mX, mY, mRadius,
+                mColorLongs, mPositions, mTileMode.nativeInt,
+                colorSpace().getNativeInstance());
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected Shader copy() {
-        final RadialGradient copy;
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            copy = new RadialGradient(mX, mY, mRadius, mColors.clone(),
-                    mPositions != null ? mPositions.clone() : null, mTileMode);
-        } else { // TYPE_COLOR_CENTER_AND_COLOR_EDGE
-            copy = new RadialGradient(mX, mY, mRadius, mCenterColor, mEdgeColor, mTileMode);
-        }
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
-    private static native long nativeCreate1(long matrix, float x, float y, float radius,
-            int colors[], float positions[], int tileMode);
-    private static native long nativeCreate2(long matrix, float x, float y, float radius,
-            int color0, int color1, int tileMode);
+    private static native long nativeCreate(long matrix, float x, float y, float radius,
+            @ColorLong long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
 }
 
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 40bcc9e..d555128 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -39,7 +41,32 @@
      * @deprecated Use subclass constructors directly instead.
      */
     @Deprecated
-    public Shader() {}
+    public Shader() {
+        mColorSpace = null;
+    }
+
+    /**
+     * @hide
+     */
+    public Shader(ColorSpace colorSpace) {
+        mColorSpace = colorSpace;
+        if (colorSpace == null) {
+            throw new IllegalArgumentException(
+                    "Use Shader() to create a Shader with no ColorSpace");
+        }
+
+        // This just ensures that if the ColorSpace is invalid, the Exception will be thrown now.
+        mColorSpace.getNativeInstance();
+    }
+
+    private final ColorSpace mColorSpace;
+
+    /**
+     * @hide
+     */
+    protected ColorSpace colorSpace() {
+        return mColorSpace;
+    }
 
     /**
      * Current native shader instance. Created and updated lazily when {@link #getNativeInstance()}
@@ -135,21 +162,6 @@
     protected void verifyNativeInstance() {
     }
 
-    /**
-     * @hide
-     */
-    protected Shader copy() {
-        final Shader copy = new Shader();
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
-    /**
-     * @hide
-     */
-    protected void copyLocalMatrix(Shader dest) {
-        dest.mLocalMatrix.set(mLocalMatrix);
-    }
 
     /**
      * @hide
@@ -169,6 +181,43 @@
         return mNativeInstance;
     }
 
+    /**
+     * @hide
+     */
+    public static @ColorLong long[] convertColors(@NonNull @ColorInt int[] colors) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+
+        long[] colorLongs = new long[colors.length];
+        for (int i = 0; i < colors.length; ++i) {
+            colorLongs[i] = Color.pack(colors[i]);
+        }
+
+        return colorLongs;
+    }
+
+    /**
+     * Detect the ColorSpace that the {@code colors} share.
+     *
+     * @throws IllegalArgumentException if the colors do not all share the same,
+     *      valid ColorSpace, or if there are less than 2 colors.
+     *
+     * @hide
+     */
+    public static ColorSpace detectColorSpace(@NonNull @ColorLong long[] colors) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        final ColorSpace colorSpace = Color.colorSpace(colors[0]);
+        for (int i = 1; i < colors.length; ++i) {
+            if (Color.colorSpace(colors[i]) != colorSpace) {
+                throw new IllegalArgumentException("All colors must be in the same ColorSpace!");
+            }
+        }
+        return colorSpace;
+    }
+
     private static native long nativeGetFinalizer();
 
 }
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index f944d85..667f45a 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -17,34 +17,52 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 
 public class SweepGradient extends Shader {
-
-    private static final int TYPE_COLORS_AND_POSITIONS = 1;
-    private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
-
-    /**
-     * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
-     * TYPE_COLOR_START_AND_COLOR_END.
-     */
-    private int mType;
-
     @UnsupportedAppUsage
     private float mCx;
     @UnsupportedAppUsage
     private float mCy;
     @UnsupportedAppUsage
+    private float[] mPositions;
+
+    // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+    @UnsupportedAppUsage
+    @ColorInt
     private int[] mColors;
     @UnsupportedAppUsage
-    private float[] mPositions;
-    @UnsupportedAppUsage
+    @ColorInt
     private int mColor0;
     @UnsupportedAppUsage
+    @ColorInt
     private int mColor1;
 
+    @ColorLong
+    private final long[] mColorLongs;
+
+    /**
+     * A Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
+     * @param colors   The sRGB colors to be distributed between around the center.
+     *                 There must be at least 2 colors in the array.
+     * @param positions May be NULL. The relative position of
+     *                 each corresponding color in the colors array, beginning
+     *                 with 0 and ending with 1.0. If the values are not
+     *                 monotonic, the drawing may produce unexpected results.
+     *                 If positions is NULL, then the colors are automatically
+     *                 spaced evenly.
+     */
+    public SweepGradient(float cx, float cy, @NonNull @ColorInt int[] colors,
+            @Nullable float[] positions) {
+        this(cx, cy, convertColors(colors), positions, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
     /**
      * A Shader that draws a sweep gradient around a center point.
      *
@@ -58,20 +76,30 @@
      *                 monotonic, the drawing may produce unexpected results.
      *                 If positions is NULL, then the colors are automatically
      *                 spaced evenly.
+     * @throws IllegalArgumentException if there are less than two colors, the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one, or {@code positions}
+     *      is not {@code null} and has a different length from {@code colors}.
      */
-    public SweepGradient(float cx, float cy,
-            @NonNull @ColorInt int colors[], @Nullable float positions[]) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
+    public SweepGradient(float cx, float cy, @NonNull @ColorLong long[] colors,
+            @Nullable float[] positions) {
+        this(cx, cy, colors.clone(), positions, detectColorSpace(colors));
+    }
+
+    /**
+     * Base constructor. Assumes @param colors is a copy that this object can hold onto,
+     * and all colors share @param colorSpace.
+     */
+    private SweepGradient(float cx, float cy, @NonNull @ColorLong long[] colors,
+            @Nullable float[] positions, ColorSpace colorSpace) {
+        super(colorSpace);
+
         if (positions != null && colors.length != positions.length) {
             throw new IllegalArgumentException(
                     "color and position arrays must be of equal length");
         }
-        mType = TYPE_COLORS_AND_POSITIONS;
         mCx = cx;
         mCy = cy;
-        mColors = colors.clone();
+        mColorLongs = colors;
         mPositions = positions != null ? positions.clone() : null;
     }
 
@@ -80,47 +108,35 @@
      *
      * @param cx       The x-coordinate of the center
      * @param cy       The y-coordinate of the center
+     * @param color0   The sRGB color to use at the start of the sweep
+     * @param color1   The sRGB color to use at the end of the sweep
+     */
+    public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
+        this(cx, cy, Color.pack(color0), Color.pack(color1));
+    }
+
+    /**
+     * A Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
      * @param color0   The color to use at the start of the sweep
      * @param color1   The color to use at the end of the sweep
+     *
+     * @throws IllegalArgumentException if the colors do
+     *      not share the same {@link ColorSpace} or do not use a valid one.
      */
-    public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
-        mType = TYPE_COLOR_START_AND_COLOR_END;
-        mCx = cx;
-        mCy = cy;
-        mColor0 = color0;
-        mColor1 = color1;
-        mColors = null;
-        mPositions = null;
+    public SweepGradient(float cx, float cy, @ColorLong long color0, @ColorLong long color1) {
+        this(cx, cy, new long[] {color0, color1}, null);
     }
 
     @Override
     long createNativeInstance(long nativeMatrix) {
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            return nativeCreate1(nativeMatrix, mCx, mCy, mColors, mPositions);
-        } else { // TYPE_COLOR_START_AND_COLOR_END
-            return nativeCreate2(nativeMatrix, mCx, mCy, mColor0, mColor1);
-        }
+        return nativeCreate(nativeMatrix, mCx, mCy, mColorLongs, mPositions,
+                colorSpace().getNativeInstance());
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected Shader copy() {
-        final SweepGradient copy;
-        if (mType == TYPE_COLORS_AND_POSITIONS) {
-            copy = new SweepGradient(mCx, mCy, mColors.clone(),
-                    mPositions != null ? mPositions.clone() : null);
-        } else { // TYPE_COLOR_START_AND_COLOR_END
-            copy = new SweepGradient(mCx, mCy, mColor0, mColor1);
-        }
-        copyLocalMatrix(copy);
-        return copy;
-    }
-
-    private static native long nativeCreate1(long matrix, float x, float y,
-            int colors[], float positions[]);
-    private static native long nativeCreate2(long matrix, float x, float y,
-            int color0, int color1);
+    private static native long nativeCreate(long matrix, float x, float y,
+            long[] colors, float[] positions, long colorSpaceHandle);
 }
 
diff --git a/keystore/java/android/security/OWNERS b/keystore/java/android/security/OWNERS
new file mode 100644
index 0000000..ed30587
--- /dev/null
+++ b/keystore/java/android/security/OWNERS
@@ -0,0 +1 @@
+per-file *.java,*.aidl = eranm@google.com,pgrafov@google.com,rubinxu@google.com
diff --git a/keystore/tests/OWNERS b/keystore/tests/OWNERS
new file mode 100644
index 0000000..9e65f88
--- /dev/null
+++ b/keystore/tests/OWNERS
@@ -0,0 +1,5 @@
+# Android Enterprise security team
+eranm@google.com
+irinaid@google.com
+pgrafov@google.com
+rubinxu@google.com
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 96798f9..0335a7c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -28,10 +28,6 @@
 
         // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
         "-Wno-missing-braces",
-
-        // TODO: Linear blending should be enabled by default, but we are
-        // TODO: making it an opt-in while it's a work in progress
-        //"-DANDROID_ENABLE_LINEAR_BLENDING",
     ],
 
     include_dirs: [
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 39bfcdd..aeeb32c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -86,7 +86,7 @@
     return sEglManager.eglDisplay();
 }
 
-static bool hasFP16Support() {
+bool HardwareBitmapUploader::hasFP16Support() {
     static std::once_flag sOnce;
     static bool hasFP16Support = false;
 
@@ -116,7 +116,6 @@
 
 static FormatInfo determineFormat(const SkBitmap& skBitmap) {
     FormatInfo formatInfo;
-    // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
     switch (skBitmap.info().colorType()) {
         case kRGBA_8888_SkColorType:
             formatInfo.isSupported = true;
@@ -127,7 +126,7 @@
             formatInfo.type = GL_UNSIGNED_BYTE;
             break;
         case kRGBA_F16_SkColorType:
-            formatInfo.isSupported = hasFP16Support();
+            formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
             if (formatInfo.isSupported) {
                 formatInfo.type = GL_HALF_FLOAT;
                 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 40f2b0c..6f41e6d 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -20,10 +20,12 @@
 
 namespace android::uirenderer {
 
-class HardwareBitmapUploader {
+class ANDROID_API HardwareBitmapUploader {
 public:
     static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
     static void terminate();
+
+    static bool hasFP16Support();
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 6dc9d34..8594766 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -759,6 +759,8 @@
 void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) {
     this->resetCanvas(bounds.right(), bounds.bottom());
     fDL = dl;
+    mClipMayBeComplex = false;
+    mSaveCount = mComplexSaveCount = 0;
 }
 
 sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
@@ -770,6 +772,7 @@
 }
 
 void RecordingCanvas::willSave() {
+    mSaveCount++;
     fDL->save();
 }
 SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
@@ -778,6 +781,11 @@
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 void RecordingCanvas::willRestore() {
+    mSaveCount--;
+    if (mSaveCount < mComplexSaveCount) {
+        mClipMayBeComplex = false;
+        mComplexSaveCount = 0;
+    }
     fDL->restore();
 }
 
@@ -798,17 +806,27 @@
 
 void RecordingCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle style) {
     fDL->clipRect(rect, op, style == kSoft_ClipEdgeStyle);
+    if (!getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     this->INHERITED::onClipRect(rect, op, style);
 }
 void RecordingCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) {
+    if (rrect.getType() > SkRRect::kRect_Type || !getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     fDL->clipRRect(rrect, op, style == kSoft_ClipEdgeStyle);
     this->INHERITED::onClipRRect(rrect, op, style);
 }
 void RecordingCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle style) {
+    setClipMayBeComplex();
     fDL->clipPath(path, op, style == kSoft_ClipEdgeStyle);
     this->INHERITED::onClipPath(path, op, style);
 }
 void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
+    if (region.isComplex() || !getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     fDL->clipRegion(region, op);
     this->INHERITED::onClipRegion(region, op);
 }
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index caaef67..3a76ca1 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -203,10 +203,41 @@
 
     void drawVectorDrawable(VectorDrawableRoot* tree);
 
+    /**
+     * If "isClipMayBeComplex" returns false, it is guaranteed the current clip is a rectangle.
+     * If the return value is true, then clip may or may not be complex (there is no guarantee).
+     */
+    inline bool isClipMayBeComplex() { return mClipMayBeComplex; }
+
 private:
     typedef SkCanvasVirtualEnforcer<SkNoDrawCanvas> INHERITED;
 
+    inline void setClipMayBeComplex() {
+        if (!mClipMayBeComplex) {
+            mComplexSaveCount = mSaveCount;
+            mClipMayBeComplex = true;
+        }
+    }
+
     DisplayListData* fDL;
+
+    /**
+     * mClipMayBeComplex tracks if the current clip is a rectangle. This flag is used to promote
+     * FunctorDrawable to a layer, if it is clipped by a non-rect.
+     */
+    bool mClipMayBeComplex = false;
+
+    /**
+     * mSaveCount is the current level of our save tree.
+     */
+    int mSaveCount = 0;
+
+    /**
+     * mComplexSaveCount is the first save level, which has a complex clip. Every level below
+     * mComplexSaveCount is assumed to have a complex clip and every level above mComplexSaveCount
+     * is guaranteed to not be complex.
+     */
+    int mComplexSaveCount = 0;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ddb7e4e..e6710cc 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -151,6 +151,7 @@
         // parent may have already dictated that a descendant layer is needed
         bool functorsNeedLayer =
                 ancestorDictatesFunctorsNeedLayer
+                || CC_UNLIKELY(isClipMayBeComplex())
 
                 // Round rect clipping forces layer for functors
                 || CC_UNLIKELY(getOutline().willRoundRectClip()) ||
@@ -193,6 +194,12 @@
 
     bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; }
 
+    bool setClipMayBeComplex(bool isClipMayBeComplex) {
+        return RP_SET(mPrimitiveFields.mClipMayBeComplex, isClipMayBeComplex);
+    }
+
+    bool isClipMayBeComplex() const { return mPrimitiveFields.mClipMayBeComplex; }
+
     bool setStaticMatrix(const SkMatrix* matrix) {
         delete mStaticMatrix;
         if (matrix) {
@@ -563,6 +570,7 @@
         bool mProjectBackwards = false;
         bool mProjectionReceiver = false;
         bool mAllowForceDark = true;
+        bool mClipMayBeComplex = false;
         Rect mClipBounds;
         Outline mOutline;
         RevealClip mRevealClip;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 7265692..da905cf 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -606,12 +606,7 @@
 
 bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
     if (!canReuseBitmap(cache.bitmap.get(), width, height)) {
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
-        sk_sp<SkColorSpace> colorSpace = nullptr;
-#else
-        sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
-#endif
-        SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
+        SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
         cache.bitmap = Bitmap::allocateHeapBitmap(info);
         return true;
     }
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 230065c..29d5ef2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -73,7 +73,6 @@
         RenderNode* childNode = child.getRenderNode();
         Matrix4 mat4(child.getRecordedMatrix());
         info.damageAccumulator->pushTransform(&mat4);
-        // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
         info.hasBackwardProjectedNodes = false;
         childFn(childNode, observer, info, functorsNeedLayer);
         hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index d54275f..d945635 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -113,6 +113,10 @@
     // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
     mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
     auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // Put Vulkan WebViews with non-rectangular clips in a HW layer
+        renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
+    }
     drawDrawable(&renderNodeDrawable);
 
     // use staging property, since recording on UI thread
@@ -138,10 +142,8 @@
 void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
-        // interop is disabled.
         functorDrawable =
-                mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+                mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
     } else {
         functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
     }
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
index 8fb621d..e783f38 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
@@ -262,12 +262,7 @@
 }
 
 sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
-    sk_sp<SkColorSpace> colorSpace = nullptr;
-#else
-    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
-#endif
-    SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
+    SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
     // This must have a top-left origin so that calls to surface->canvas->writePixels
     // performs a basic texture upload instead of a more complex drawing operation
     return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 4a2f57e..624c290 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -46,8 +46,8 @@
 }
 
 void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
-                                              android_dataspace dataspace) {
-    if (!mImage.get() || dataspace != mDataspace) {
+                                              android_dataspace dataspace, bool forceCreate) {
+    if (!mImage.get() || dataspace != mDataspace || forceCreate) {
         mImage = graphicBuffer.get()
                          ? SkImage::MakeFromAHardwareBuffer(
                                    reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
@@ -71,7 +71,7 @@
             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
                 *queueEmpty = true;
                 mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
-                        st.mCurrentDataSpace);
+                        st.mCurrentDataSpace, false);
                 return mImageSlots[slot].mImage;
             }
         }
@@ -150,7 +150,7 @@
     st.computeCurrentTransformMatrixLocked();
 
     *queueEmpty = false;
-    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true);
     return mImageSlots[slot].mImage;
 }
 
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index f0e55bb..5c41903 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -82,7 +82,8 @@
          */
         EGLSyncKHR mEglFence;
 
-        void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace);
+        void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace,
+                            bool forceCreate);
     };
 
     /**
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index b67d10d..79400de 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -82,17 +82,6 @@
     return linear <= 0.0031308f ? linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f;
 }
 
-// Opto-electronic conversion function for the sRGB color space
-// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
-// This function returns the input unmodified if linear blending is not enabled
-static constexpr float OECF(float linear) {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
-    return OECF_sRGB(linear);
-#else
-    return linear;
-#endif
-}
-
 // Electro-optical conversion function for the sRGB color space
 // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
 static constexpr float EOCF_sRGB(float srgb) {
@@ -100,17 +89,6 @@
     return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f);
 }
 
-// Electro-optical conversion function for the sRGB color space
-// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
-// This function returns the input unmodified if linear blending is not enabled
-static constexpr float EOCF(float srgb) {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
-    return EOCF_sRGB(srgb);
-#else
-    return srgb;
-#endif
-}
-
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format);
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 59c6a0a..63b57d1 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -181,22 +181,14 @@
         "android.location.GPS_ENABLED_CHANGE";
 
     /**
-     * Broadcast intent action when the configured location providers
-     * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
-     * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
-     * instead.
+     * Broadcast intent action when the set of enabled location providers changes. To check the
+     * status of a provider, use {@link #isProviderEnabled(String)}.
      */
-    public static final String PROVIDERS_CHANGED_ACTION =
-        "android.location.PROVIDERS_CHANGED";
+    public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
 
     /**
-     * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
-     * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
-     * If you're interacting with {@link #isProviderEnabled(String)}, use
-     * {@link #PROVIDERS_CHANGED_ACTION} instead.
-     *
-     * In the future, there may be mode changes that do not result in
-     * {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
+     * Broadcast intent action when the device location mode changes. To check the location mode,
+     * use {@link #isLocationEnabled()}.
      */
     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
 
@@ -207,8 +199,10 @@
      * If you're interacting with {@link #isProviderEnabled(String)}, use
      * {@link #PROVIDERS_CHANGED_ACTION} instead.
      *
+     * @deprecated Do not use.
      * @hide
      */
+    @Deprecated
     public static final String MODE_CHANGING_ACTION = "com.android.settings.location.MODE_CHANGING";
 
     /**
@@ -299,7 +293,7 @@
             "com.android.settings.location.FOOTER_STRING";
 
     // Map from LocationListeners to their associated ListenerTransport objects
-    private HashMap<LocationListener,ListenerTransport> mListeners =
+    private final HashMap<LocationListener, ListenerTransport> mListeners =
         new HashMap<LocationListener,ListenerTransport>();
 
     private class ListenerTransport extends ILocationListener.Stub {
@@ -1264,39 +1258,20 @@
     }
 
     /**
-     * Returns the current enabled/disabled status of location
+     * Returns the current enabled/disabled state of location. To listen for changes, see
+     * {@link #MODE_CHANGED_ACTION}.
      *
-     * @return true if location is enabled. false if location is disabled.
+     * @return true if location is enabled and false if location is disabled.
      */
     public boolean isLocationEnabled() {
         return isLocationEnabledForUser(Process.myUserHandle());
     }
 
     /**
-     * Method for enabling or disabling location.
-     *
-     * @param enabled true to enable location. false to disable location
-     * @param userHandle the user to set
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(WRITE_SECURE_SETTINGS)
-    public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        Settings.Secure.putIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_MODE,
-                enabled
-                        ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
-                        : Settings.Secure.LOCATION_MODE_OFF,
-                userHandle.getIdentifier());
-    }
-
-    /**
-     * Returns the current enabled/disabled status of location
+     * Returns the current enabled/disabled state of location.
      *
      * @param userHandle the user to query
-     * @return true location is enabled. false if location is disabled.
+     * @return true if location is enabled and false if location is disabled.
      *
      * @hide
      */
@@ -1310,19 +1285,32 @@
     }
 
     /**
-     * Returns the current enabled/disabled status of the given provider.
+     * Enables or disables the location setting.
      *
-     * <p>If the user has enabled this provider in the Settings menu, true
-     * is returned otherwise false is returned
+     * @param enabled true to enable location and false to disable location.
+     * @param userHandle the user to set
      *
-     * <p>Callers should instead use {@link #isLocationEnabled()}
-     * unless they depend on provider-specific APIs such as
-     * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_MODE,
+                enabled
+                        ? Settings.Secure.LOCATION_MODE_ON
+                        : Settings.Secure.LOCATION_MODE_OFF,
+                userHandle.getIdentifier());
+    }
+
+    /**
+     * Returns the current enabled/disabled status of the given provider. To listen for changes, see
+     * {@link #PROVIDERS_CHANGED_ACTION}.
      *
-     * <p>
-     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
-     * method would throw {@link SecurityException} if the location permissions
-     * were not sufficient to use the specified provider.
+     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
+     * {@link SecurityException} if the location permissions were not sufficient to use the
+     * specified provider.
      *
      * @param provider the name of the provider
      * @return true if the provider exists and is enabled
@@ -1334,19 +1322,13 @@
     }
 
     /**
-     * Returns the current enabled/disabled status of the given provider and user.
+     * Returns the current enabled/disabled status of the given provider and user. Callers should
+     * prefer {@link #isLocationEnabledForUser(UserHandle)} unless they depend on provider-specific
+     * APIs.
      *
-     * <p>If the user has enabled this provider in the Settings menu, true
-     * is returned otherwise false is returned
-     *
-     * <p>Callers should instead use {@link #isLocationEnabled()}
-     * unless they depend on provider-specific APIs such as
-     * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
-     *
-     * <p>
-     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
-     * method would throw {@link SecurityException} if the location permissions
-     * were not sufficient to use the specified provider.
+     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
+     * {@link SecurityException} if the location permissions were not sufficient to use the
+     * specified provider.
      *
      * @param provider the name of the provider
      * @param userHandle the user to query
@@ -1367,12 +1349,14 @@
     }
 
     /**
-     * Method for enabling or disabling a single location provider.
+     * Method for enabling or disabling a single location provider. This method is deprecated and
+     * functions as a best effort. It should not be relied on in any meaningful sense as providers
+     * may no longer be enabled or disabled by clients.
      *
      * @param provider the name of the provider
      * @param enabled true to enable the provider. false to disable the provider
      * @param userHandle the user to set
-     * @return true if the value was set, false on database errors
+     * @return true if the value was set, false otherwise
      *
      * @throws IllegalArgumentException if provider is null
      * @deprecated Do not manipulate providers individually, use
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 3d0afb09..0480eab 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -683,6 +683,7 @@
                     request.setSmallestDisplacement(in.readFloat());
                     request.setHideFromAppOps(in.readInt() != 0);
                     request.setLowPowerMode(in.readInt() != 0);
+                    request.setLocationSettingsIgnored(in.readInt() != 0);
                     String provider = in.readString();
                     if (provider != null) request.setProvider(provider);
                     WorkSource workSource = in.readParcelable(null);
@@ -711,6 +712,7 @@
         parcel.writeFloat(mSmallestDisplacement);
         parcel.writeInt(mHideFromAppOps ? 1 : 0);
         parcel.writeInt(mLowPowerMode ? 1 : 0);
+        parcel.writeInt(mLocationSettingsIgnored ? 1 : 0);
         parcel.writeString(mProvider);
         parcel.writeParcelable(mWorkSource, 0);
     }
@@ -755,6 +757,9 @@
             s.append(" num=").append(mNumUpdates);
         }
         s.append(" lowPowerMode=").append(mLowPowerMode);
+        if (mLocationSettingsIgnored) {
+            s.append(" ignoreSettings");
+        }
         s.append(']');
         return s.toString();
     }
diff --git a/media/Android.bp b/media/Android.bp
index e7d5faf..1aefebe 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,35 +1,8 @@
 java_library {
-    name: "updatable-media1",
-
-    srcs: [
-        ":media1-srcs",
-        ":framework-media-annotation-srcs",
-    ],
-
-    aidl: {
-        export_include_dirs: [
-            "apex/java",
-        ],
-
-        // TODO: find out a way to include only the necessary aidl files instead of dirs.
-        include_dirs: [
-            "frameworks/base/core/java",
-            "frameworks/base/media/java",
-        ],
-    },
-
-    installable: true,
-
-    // Make sure that the implementaion only relies on SDK or system APIs.
-    sdk_version: "system_current",
-}
-
-java_library {
     name: "updatable-media",
 
     srcs: [
-        ":mediaplayer2-srcs",
-        ":mediasession2-srcs",
+        ":updatable-media-srcs",
         ":framework-media-annotation-srcs",
     ],
 
@@ -38,8 +11,12 @@
             "apex/java",
         ],
 
+        // It would be great if we don't need to add include_dirs for public
+        // parcelable classes. Find a better way.
         include_dirs: [
-            // For the usage of android.os.Bundle and android.os.ResultReceiver in aidl files
+            // To refer:
+            // android.os.Bundle
+            // android.os.ResultReceiver
             "frameworks/base/core/java",
         ],
     },
@@ -55,53 +32,18 @@
 }
 
 filegroup {
-    name: "media-srcs-without-aidls",
-    srcs : [
-        ":media1-srcs-without-aidls",
-        ":mediasession2-srcs-without-aidls",
+    name: "updatable-media-srcs",
+    srcs: [
+        ":mediasession2-srcs",
         ":mediaplayer2-srcs",
     ],
 }
 
 filegroup {
-    name: "media1-srcs",
-    srcs: [
-        "apex/java/android/media/MediaDescription.java",
-        "apex/java/android/media/MediaParceledListSlice.java",
-        "apex/java/android/media/Rating.java",
-        "apex/java/android/media/VolumeProvider.java",
-        "apex/java/android/media/browse/MediaBrowser.java",
-        "apex/java/android/media/browse/MediaBrowserUtils.java",
-        "apex/java/android/media/session/ControllerCallbackLink.java",
-        "apex/java/android/media/session/ControllerLink.java",
-        "apex/java/android/media/session/ISession.aidl",
-        "apex/java/android/media/session/ISessionCallback.aidl",
-        "apex/java/android/media/session/ISessionController.aidl",
-        "apex/java/android/media/session/ISessionControllerCallback.aidl",
-        "apex/java/android/media/session/MediaController.java",
-        "apex/java/android/media/session/MediaSessionEngine.java",
-        "apex/java/android/media/session/MediaSessionProviderService.java",
-        "apex/java/android/media/session/PlaybackState.java",
-        "apex/java/android/media/session/SessionCallbackLink.java",
-        "apex/java/android/media/session/SessionLink.java",
-        "apex/java/android/service/media/IMediaBrowserService.aidl",
-        "apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
-        "apex/java/android/service/media/MediaBrowserService.java",
-    ],
-}
-
-filegroup {
-    name: "media1-srcs-without-aidls",
-    srcs: [
-        ":media1-srcs",
-    ],
-    exclude_srcs: [
-        "apex/java/android/media/session/ISession.aidl",
-        "apex/java/android/media/session/ISessionCallback.aidl",
-        "apex/java/android/media/session/ISessionController.aidl",
-        "apex/java/android/media/session/ISessionControllerCallback.aidl",
-        "apex/java/android/service/media/IMediaBrowserService.aidl",
-        "apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+    name: "updatable-media-srcs-without-aidls",
+    srcs : [
+        ":mediasession2-srcs-without-aidls",
+        ":mediaplayer2-srcs",
     ],
 }
 
@@ -120,6 +62,7 @@
         "apex/java/android/media/Session2Command.java",
         "apex/java/android/media/Session2CommandGroup.java",
         "apex/java/android/media/Session2Link.java",
+        "apex/java/android/media/Session2Token.java",
     ],
 }
 
@@ -144,7 +87,6 @@
         "apex/java/android/media/UriDataSourceDesc.java",
         "apex/java/android/media/FileDataSourceDesc.java",
         "apex/java/android/media/CallbackDataSourceDesc.java",
-        "apex/java/android/media/VideoSize.java",
         "apex/java/android/media/Media2Utils.java",
         "apex/java/android/media/MediaPlayer2Utils.java",
         "apex/java/android/media/MediaPlayer2.java",
@@ -154,3 +96,32 @@
         "apex/java/android/media/BufferingParams.java",
     ],
 }
+
+metalava_updatable_media_args = " --error UnhiddenSystemApi " +
+    "--hide RequiresPermission " +
+    "--hide MissingPermission --hide BroadcastBehavior " +
+    "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+    "--hide HiddenTypedefConstant "
+
+droidstubs {
+    name: "updatable-media-stubs",
+    srcs: [
+        ":updatable-media-srcs-without-aidls",
+        ":framework-media-annotation-srcs",
+    ],
+    args: metalava_updatable_media_args + " --show-annotation android.annotation.SystemApi " +
+        " --show-annotation android.annotation.TestApi ",
+    // Ideally, sdk_version here should be "current_system", but "current - 1" is used
+    // to avoid dependency cycle with framework.
+    sdk_version: "28",
+}
+
+java_library {
+    name: "updatable_media_stubs",
+    srcs: [":updatable-media-stubs"],
+    // Ideally, sdk_version here should be "current_system", but "current - 1" is used
+    // to avoid dependency cycle with framework.
+    sdk_version: "28",
+}
+
diff --git a/media/apex/java/android/media/MediaConstants.java b/media/apex/java/android/media/MediaConstants.java
index 45ea826..65b6f55 100644
--- a/media/apex/java/android/media/MediaConstants.java
+++ b/media/apex/java/android/media/MediaConstants.java
@@ -24,8 +24,7 @@
     static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
 
     // Bundle key for Parcelable
-    static final String KEY_SESSION2_TOKEN = "android.media.key.SESSION2_TOKEN";
-    static final String KEY_SESSION2_LINK = "android.media.key.SESSION2_LINK";
+    static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
     static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
     static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
 
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 41721f3..887b447 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -20,11 +20,9 @@
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
 import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2_LINK;
-import static android.media.MediaConstants.KEY_SESSION2_TOKEN;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.SESSION_SERVICE_INTERFACE;
 import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
@@ -262,8 +260,7 @@
 
     // Called by Controller2Link.onConnected
     void onConnected(int seq, Bundle connectionResult) {
-        Session2Token token = connectionResult.getParcelable(KEY_SESSION2_TOKEN);
-        Session2Link sessionBinder = token.getExtras().getParcelable(KEY_SESSION2_LINK);
+        Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
         Session2CommandGroup allowedCommands =
                 connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
         boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
@@ -284,7 +281,8 @@
             // Implementation for the local binder is no-op,
             // so can be used without worrying about deadlock.
             sessionBinder.linkToDeath(mDeathRecipient, 0);
-            mConnectedToken = token;
+            mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
+                    mSessionToken.getPackageName(), sessionBinder);
         }
         mCallbackExecutor.execute(() -> {
             mCallback.onConnected(MediaController2.this, allowedCommands);
@@ -355,7 +353,7 @@
     }
 
     private boolean requestConnectToSession() {
-        Session2Link sessionBinder = mSessionToken.getExtras().getParcelable(KEY_SESSION2_LINK);
+        Session2Link sessionBinder = mSessionToken.getSessionLink();
         Bundle connectionRequest = createConnectionRequest();
         try {
             sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
@@ -368,7 +366,7 @@
 
     private boolean requestConnectToService() {
         // Service. Needs to get fresh binder whenever connection is needed.
-        final Intent intent = new Intent(SESSION_SERVICE_INTERFACE);
+        final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE);
         intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName());
 
         // Use bindService() instead of startForegroundService() to start session service for three
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index 1a1f6fb..925ca0d 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -40,6 +41,7 @@
 import android.os.PowerManager;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 
@@ -61,7 +63,6 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -299,7 +300,7 @@
     private final AtomicLong mSrcIdGenerator = new AtomicLong(0);
 
     private volatile float mVolume = 1.0f;
-    private VideoSize mVideoSize = new VideoSize(0, 0);
+    private Size mVideoSize = new Size(0, 0);
 
     private static ExecutorService sDrmThreadPool = Executors.newCachedThreadPool();
 
@@ -1526,7 +1527,7 @@
      * notification {@code EventCallback.onVideoSizeChanged} when the size
      * is available.
      */
-    public VideoSize getVideoSize() {
+    public Size getVideoSize() {
         return mVideoSize;
     }
 
@@ -2526,7 +2527,7 @@
                     final int width = msg.arg1;
                     final int height = msg.arg2;
 
-                    mVideoSize = new VideoSize(width, height);
+                    mVideoSize = new Size(width, height);
                     sendEvent(new EventNotifier() {
                         @Override
                         public void notify(EventCallback callback) {
@@ -2765,7 +2766,7 @@
          * @param size the size of the video
          */
         public void onVideoSizeChanged(
-                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull VideoSize size) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull Size size) { }
 
         /**
          * Called to indicate an avaliable timed text
@@ -3667,53 +3668,66 @@
     }
 
     /**
-     * The status codes for {@link DrmEventCallback#onDrmPrepared} listener.
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
      * <p>
      *
      * DRM preparation has succeeded.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * The device required DRM provisioning but couldn't reach the provisioning server.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * The device required DRM provisioning but the provisioning server denied the request.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * The DRM preparation has failed .
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * The crypto scheme UUID is not supported by the device.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * The hardware resources are not available, due to being in use.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * Restoring persisted offline keys failed.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_RESTORE_ERROR = 6;
 
     /**
+     * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
+     * <p>
+     *
      * Error during key request/response exchange with license server.
-     * @hide
      */
     public static final int PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR = 7;
 
@@ -3758,6 +3772,7 @@
      * @throws IllegalStateException if called before being prepared
      * @hide
      */
+    @TestApi
     public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
         final SourceInfo sourceInfo = getSourceInfo(dsd);
         if (sourceInfo != null) {
@@ -3814,6 +3829,7 @@
      * @hide
      */
     // This is an asynchronous call.
+    @TestApi
     public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
         return addTask(newPrepareDrmTask(dsd, uuid));
     }
@@ -3905,6 +3921,7 @@
      * @hide
      */
     // This is a synchronous call.
+    @TestApi
     public void releaseDrm(@NonNull DataSourceDesc dsd)
             throws NoDrmSchemeException {
         final SourceInfo sourceInfo = getSourceInfo(dsd);
@@ -3955,6 +3972,7 @@
      * @throws NoDrmSchemeException if there is no active DRM session
      * @hide
      */
+    @TestApi
     public MediaDrm.KeyRequest getDrmKeyRequest(
             @NonNull DataSourceDesc dsd,
             @Nullable byte[] keySetId, @Nullable byte[] initData,
@@ -3997,6 +4015,7 @@
      * @hide
      */
     // This is a synchronous call.
+    @TestApi
     public byte[] provideDrmKeyResponse(
             @NonNull DataSourceDesc dsd,
             @Nullable byte[] keySetId, @NonNull byte[] response)
@@ -4023,6 +4042,7 @@
      * @hide
      */
     // This is a synchronous call.
+    @TestApi
     public void restoreDrmKeys(
             @NonNull DataSourceDesc dsd,
             @NonNull byte[] keySetId)
@@ -4050,6 +4070,7 @@
      * @throws NoDrmSchemeException if there is no active DRM session
      * @hide
      */
+    @TestApi
     public String getDrmPropertyString(
             @NonNull DataSourceDesc dsd,
             @NonNull @MediaDrmStringProperty String propertyName)
@@ -4078,6 +4099,7 @@
      * @hide
      */
     // This is a synchronous call.
+    @TestApi
     public void setDrmPropertyString(
             @NonNull DataSourceDesc dsd,
             @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index 80c91cc..fdd07fd 100644
--- a/media/apex/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
@@ -20,10 +20,10 @@
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
 import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2_LINK;
-import static android.media.MediaConstants.KEY_SESSION2_TOKEN;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
+import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,6 +34,7 @@
 import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Process;
 import android.os.ResultReceiver;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -107,10 +108,8 @@
         mCallbackExecutor = callbackExecutor;
         mCallback = callback;
         mSessionStub = new Session2Link(this);
-
-        Bundle extras = new Bundle();
-        extras.putParcelable(KEY_SESSION2_LINK, mSessionStub);
-        mSessionToken = new Session2Token(context, id, extras);
+        mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
+                mSessionStub);
         mSessionManager = (MediaSessionManager) mContext.getSystemService(
                 Context.MEDIA_SESSION_SERVICE);
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
@@ -142,8 +141,6 @@
             for (ControllerInfo info : controllerInfos) {
                 info.notifyDisconnected();
             }
-            mSessionToken.destroy();
-            mSessionManager.notifySession2Destroyed(mSessionToken);
         } catch (Exception e) {
             // Should not be here.
         }
@@ -331,7 +328,7 @@
                 // It's needed because we cannot call synchronous calls between
                 // session/controller.
                 Bundle connectionResult = new Bundle();
-                connectionResult.putParcelable(KEY_SESSION2_TOKEN, mSessionToken);
+                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
                 connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
                         controllerInfo.mAllowedCommands);
                 connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java
index 54d0ed2..5bb746a 100644
--- a/media/apex/java/android/media/MediaSession2Service.java
+++ b/media/apex/java/android/media/MediaSession2Service.java
@@ -48,7 +48,7 @@
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
-    public static final String SERVICE_INTERFACE = Session2Token.SESSION_SERVICE_INTERFACE;
+    public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
 
     private static final String TAG = "MediaSession2Service";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/media/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java
similarity index 70%
rename from media/java/android/media/Session2Token.java
rename to media/apex/java/android/media/Session2Token.java
index 80494ad..238cc2b 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/apex/java/android/media/Session2Token.java
@@ -19,17 +19,13 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.media.session.MediaSessionManager;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.Process;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -39,7 +35,7 @@
 import java.util.Objects;
 
 /**
- * Represents an ongoing MediaSession2 or a MediaSession2Service.
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
  * If it's representing a session service, it may not be ongoing.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -48,7 +44,7 @@
  * for consistent behavior across all devices.
  * <p>
  * This may be passed to apps by the session owner to allow them to create a
- * MediaController2 to communicate with the session.
+ * {@link MediaController2} to communicate with the session.
  * <p>
  * It can be also obtained by {@link android.media.session.MediaSessionManager}.
  */
@@ -68,13 +64,6 @@
     };
 
     /**
-     * The {@link Intent} that must be declared for the session service.
-     * @hide
-     */
-    @SystemApi
-    public static final String SESSION_SERVICE_INTERFACE = "android.media.MediaSession2Service";
-
-    /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -83,26 +72,22 @@
     }
 
     /**
-     * Type for MediaSession2.
+     * Type for {@link MediaSession2}.
      */
     public static final int TYPE_SESSION = 0;
 
     /**
-     * Type for MediaSession2Service.
+     * Type for {@link MediaSession2Service}.
      */
     public static final int TYPE_SESSION_SERVICE = 1;
 
-    private final String mSessionId;
-    private final int mPid;
     private final int mUid;
     @TokenType
     private final int mType;
     private final String mPackageName;
     private final String mServiceName;
+    private final Session2Link mSessionLink;
     private final ComponentName mComponentName;
-    private final Bundle mExtras;
-
-    private boolean mDestroyed = false;
 
     /**
      * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
@@ -121,67 +106,44 @@
         final PackageManager manager = context.getPackageManager();
         final int uid = getUid(manager, serviceComponent.getPackageName());
 
-        if (!isInterfaceDeclared(manager, SESSION_SERVICE_INTERFACE, serviceComponent)) {
+        if (!isInterfaceDeclared(manager, MediaSession2Service.SERVICE_INTERFACE,
+                serviceComponent)) {
             Log.w(TAG, serviceComponent + " doesn't implement MediaSession2Service.");
         }
-        mSessionId = null;
         mComponentName = serviceComponent;
         mPackageName = serviceComponent.getPackageName();
         mServiceName = serviceComponent.getClassName();
-        mPid = -1;
         mUid = uid;
         mType = TYPE_SESSION_SERVICE;
-        mExtras = null;
+        mSessionLink = null;
     }
 
-    /**
-     * Constructor for the token with type {@link #TYPE_SESSION}.
-     *
-     * @param context The context.
-     * @param sessionId The ID of the session. Should be unique.
-     * @param extras The extras.
-     * @hide
-     */
-    @SystemApi
-    public Session2Token(@NonNull Context context, @NonNull String sessionId,
-            @Nullable Bundle extras) {
-        if (sessionId == null) {
-            throw new IllegalArgumentException("sessionId shouldn't be null");
-        }
-        if (context == null) {
-            throw new IllegalArgumentException("context shouldn't be null");
-        }
-        mSessionId = sessionId;
-        mPid = Process.myPid();
-        mUid = Process.myUid();
-        mType = TYPE_SESSION;
-        mPackageName = context.getPackageName();
-        mExtras = extras;
+    Session2Token(int uid, int type, String packageName, Session2Link sessionLink) {
+        mUid = uid;
+        mType = type;
+        mPackageName = packageName;
         mServiceName = null;
         mComponentName = null;
+        mSessionLink = sessionLink;
     }
 
     Session2Token(Parcel in) {
-        mSessionId = in.readString();
-        mPid = in.readInt();
         mUid = in.readInt();
         mType = in.readInt();
         mPackageName = in.readString();
         mServiceName = in.readString();
+        mSessionLink = in.readParcelable(null);
         mComponentName = ComponentName.unflattenFromString(in.readString());
-        mExtras = in.readParcelable(null);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mSessionId);
-        dest.writeInt(mPid);
         dest.writeInt(mUid);
         dest.writeInt(mType);
         dest.writeString(mPackageName);
         dest.writeString(mServiceName);
+        dest.writeParcelable(mSessionLink, flags);
         dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
-        dest.writeParcelable(mExtras, flags);
     }
 
     @Override
@@ -191,7 +153,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSessionId, mPid, mUid, mType, mPackageName, mServiceName);
+        return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink);
     }
 
     @Override
@@ -200,27 +162,17 @@
             return false;
         }
         Session2Token other = (Session2Token) obj;
-        return TextUtils.equals(mSessionId, other.mSessionId)
-                && mPid == other.mPid
-                && mUid == other.mUid
-                && mType == other.mType
+        return mUid == other.mUid
                 && TextUtils.equals(mPackageName, other.mPackageName)
-                && TextUtils.equals(mServiceName, other.mServiceName);
+                && TextUtils.equals(mServiceName, other.mServiceName)
+                && mType == other.mType
+                && Objects.equals(mSessionLink, other.mSessionLink);
     }
 
     @Override
     public String toString() {
         return "Session2Token {pkg=" + mPackageName + " type=" + mType
-                + " service=" + mServiceName + "}";
-    }
-
-    /**
-     * @return pid of the session
-     * @hide
-     */
-    @SystemApi
-    public int getPid() {
-        return mPid;
+                + " service=" + mServiceName + " Session2Link=" + mSessionLink + "}";
     }
 
     /**
@@ -255,36 +207,8 @@
         return mType;
     }
 
-    /**
-     * @return extras
-     * @hide
-     */
-    @SystemApi
-    @NonNull
-    public Bundle getExtras() {
-        return mExtras == null ? new Bundle() : new Bundle(mExtras);
-    }
-
-    /**
-     * Destroys this session token. After this method is called,
-     * {@link MediaSessionManager#notifySession2Created(Session2Token)} should not be called
-     * with this token.
-     *
-     * @see MediaSessionManager#notifySession2Created(Session2Token)
-     * @hide
-     */
-    @SystemApi
-    public void destroy() {
-        mDestroyed = true;
-    }
-
-    /**
-     * @return whether this token is destroyed
-     * @hide
-     */
-    @SystemApi
-    public boolean isDestroyed() {
-        return mDestroyed;
+    Session2Link getSessionLink() {
+        return mSessionLink;
     }
 
     private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
diff --git a/media/apex/java/android/media/VideoSize.java b/media/apex/java/android/media/VideoSize.java
deleted file mode 100644
index 19631e0..0000000
--- a/media/apex/java/android/media/VideoSize.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-/**
- * Immutable class for describing width and height dimensions.
- */
-public final class VideoSize {
-    /**
-     * Create a new immutable VideoSize instance.
-     *
-     * @param width The width of the video size
-     * @param height The height of the video size
-     */
-    VideoSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    /**
-     * Get the width of the video size
-     * @return width
-     */
-    public int getWidth() {
-        return mWidth;
-    }
-
-    /**
-     * Get the height of the video size
-     * @return height
-     */
-    public int getHeight() {
-        return mHeight;
-    }
-
-    /**
-     * Check if this video size is equal to another video size.
-     * <p>
-     * Two video sizes are equal if and only if both their widths and heights are
-     * equal.
-     * </p>
-     * <p>
-     * A video size object is never equal to any other type of object.
-     * </p>
-     *
-     * @return {@code true} if the objects were equal, {@code false} otherwise
-     */
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof VideoSize) {
-            VideoSize other = (VideoSize) obj;
-            return mWidth == other.mWidth && mHeight == other.mHeight;
-        }
-        return false;
-    }
-
-    /**
-     * Return the video size represented as a string with the format {@code "WxH"}
-     *
-     * @return string representation of the video size
-     */
-    @Override
-    public String toString() {
-        return mWidth + "x" + mHeight;
-    }
-
-    private final int mWidth;
-    private final int mHeight;
-}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index ffa3b24..9218e92 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.bluetooth.BluetoothCodecConfig;
 import android.content.Context;
@@ -933,6 +934,13 @@
     /** @hide enables or disables the master mono mode. */
     public static native int setMasterMono(boolean mono);
 
+    /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */
+    @TestApi
+    public static native float getMasterBalance();
+    /** @hide changes the audio balance of the device. */
+    @TestApi
+    public static native int setMasterBalance(float balance);
+
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
     public static native int getPrimaryOutputSamplingRate();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f756658..0c3d625 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3314,7 +3314,7 @@
      */
     public void setAudioPresentation(@NonNull AudioPresentation presentation) {
         if (presentation == null) {
-            throw new IllegalArgumentException("audio presentation is null");
+            throw new NullPointerException("audio presentation is null");
         }
         native_setAudioPresentation(presentation.getPresentationId(), presentation.getProgramId());
     }
diff --git a/media/apex/java/android/media/MediaDescription.aidl b/media/java/android/media/MediaDescription.aidl
similarity index 100%
rename from media/apex/java/android/media/MediaDescription.aidl
rename to media/java/android/media/MediaDescription.aidl
diff --git a/media/apex/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
similarity index 100%
rename from media/apex/java/android/media/MediaDescription.java
rename to media/java/android/media/MediaDescription.java
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2d2c4a8..9258b85 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1186,22 +1186,22 @@
     public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0;
 
     /**
-     * Offline license state is usable, the keys may be used for decryption.
+     * Offline license is usable, the keys may be used for decryption.
      */
-    public static final int OFFLINE_LICENSE_USABLE = 1;
+    public static final int OFFLINE_LICENSE_STATE_USABLE = 1;
 
     /**
-     * Offline license state is inactive, the keys have been marked for
-     * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but the
-     * key response has not been received.
+     * Offline license is released, the keys have been marked for
+     * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but
+     * the key response has not been received.
      */
-    public static final int OFFLINE_LICENSE_INACTIVE = 2;
+    public static final int OFFLINE_LICENSE_STATE_RELEASED = 2;
 
     /** @hide */
     @IntDef({
         OFFLINE_LICENSE_STATE_UNKNOWN,
-        OFFLINE_LICENSE_USABLE,
-        OFFLINE_LICENSE_INACTIVE,
+        OFFLINE_LICENSE_STATE_USABLE,
+        OFFLINE_LICENSE_STATE_RELEASED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OfflineLicenseState {}
diff --git a/media/apex/java/android/media/MediaParceledListSlice.aidl b/media/java/android/media/MediaParceledListSlice.aidl
similarity index 100%
rename from media/apex/java/android/media/MediaParceledListSlice.aidl
rename to media/java/android/media/MediaParceledListSlice.aidl
diff --git a/media/apex/java/android/media/MediaParceledListSlice.java b/media/java/android/media/MediaParceledListSlice.java
similarity index 100%
rename from media/apex/java/android/media/MediaParceledListSlice.java
rename to media/java/android/media/MediaParceledListSlice.java
diff --git a/media/apex/java/android/media/Rating.aidl b/media/java/android/media/Rating.aidl
similarity index 100%
rename from media/apex/java/android/media/Rating.aidl
rename to media/java/android/media/Rating.aidl
diff --git a/media/apex/java/android/media/Rating.java b/media/java/android/media/Rating.java
similarity index 100%
rename from media/apex/java/android/media/Rating.java
rename to media/java/android/media/Rating.java
diff --git a/media/apex/java/android/media/VolumeProvider.java b/media/java/android/media/VolumeProvider.java
similarity index 98%
rename from media/apex/java/android/media/VolumeProvider.java
rename to media/java/android/media/VolumeProvider.java
index 49202ee..8f68cbd 100644
--- a/media/apex/java/android/media/VolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -16,7 +16,6 @@
 package android.media;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.media.session.MediaSession;
 
 import java.lang.annotation.Retention;
@@ -148,7 +147,6 @@
      * Sets a callback to receive volume changes.
      * @hide
      */
-    @SystemApi
     public void setCallback(Callback callback) {
         mCallback = callback;
     }
@@ -157,7 +155,6 @@
      * Listens for changes to the volume.
      * @hide
      */
-    @SystemApi
     public abstract static class Callback {
         /**
          * Called when volume changed.
diff --git a/media/apex/java/android/media/browse/MediaBrowser.aidl b/media/java/android/media/browse/MediaBrowser.aidl
similarity index 100%
rename from media/apex/java/android/media/browse/MediaBrowser.aidl
rename to media/java/android/media/browse/MediaBrowser.aidl
diff --git a/media/apex/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
similarity index 100%
rename from media/apex/java/android/media/browse/MediaBrowser.java
rename to media/java/android/media/browse/MediaBrowser.java
diff --git a/media/apex/java/android/media/browse/MediaBrowserUtils.java b/media/java/android/media/browse/MediaBrowserUtils.java
similarity index 100%
rename from media/apex/java/android/media/browse/MediaBrowserUtils.java
rename to media/java/android/media/browse/MediaBrowserUtils.java
diff --git a/media/apex/java/android/media/session/ControllerCallbackLink.aidl b/media/java/android/media/session/ControllerCallbackLink.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ControllerCallbackLink.aidl
rename to media/java/android/media/session/ControllerCallbackLink.aidl
diff --git a/media/apex/java/android/media/session/ControllerCallbackLink.java b/media/java/android/media/session/ControllerCallbackLink.java
similarity index 99%
rename from media/apex/java/android/media/session/ControllerCallbackLink.java
rename to media/java/android/media/session/ControllerCallbackLink.java
index adc14a5..428be0d 100644
--- a/media/apex/java/android/media/session/ControllerCallbackLink.java
+++ b/media/java/android/media/session/ControllerCallbackLink.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.MediaMetadata;
@@ -41,7 +40,6 @@
  * Handles incoming commands to {@link MediaController.Callback}.
  * @hide
  */
-@SystemApi
 public final class ControllerCallbackLink implements Parcelable {
     final Context mContext;
     final CallbackStub mCallbackStub;
diff --git a/media/apex/java/android/media/session/ControllerLink.aidl b/media/java/android/media/session/ControllerLink.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ControllerLink.aidl
rename to media/java/android/media/session/ControllerLink.aidl
diff --git a/media/apex/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java
similarity index 99%
rename from media/apex/java/android/media/session/ControllerLink.java
rename to media/java/android/media/session/ControllerLink.java
index f60ec00..64d283f 100644
--- a/media/apex/java/android/media/session/ControllerLink.java
+++ b/media/java/android/media/session/ControllerLink.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.media.MediaMetadata;
 import android.media.MediaParceledListSlice;
@@ -40,7 +39,6 @@
  * Handles incoming commands from {@link MediaController}.
  * @hide
  */
-@SystemApi
 public final class ControllerLink implements Parcelable {
     public static final Parcelable.Creator<ControllerLink> CREATOR =
             new Parcelable.Creator<ControllerLink>() {
diff --git a/media/apex/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ISession.aidl
rename to media/java/android/media/session/ISession.aidl
diff --git a/media/apex/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ISessionCallback.aidl
rename to media/java/android/media/session/ISessionCallback.aidl
diff --git a/media/apex/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ISessionController.aidl
rename to media/java/android/media/session/ISessionController.aidl
diff --git a/media/apex/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
similarity index 100%
rename from media/apex/java/android/media/session/ISessionControllerCallback.aidl
rename to media/java/android/media/session/ISessionControllerCallback.aidl
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index edaa55d..8143bfa 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -37,7 +37,6 @@
     SessionLink createSession(String packageName, in SessionCallbackLink sessionCb, String tag,
             int userId);
     void notifySession2Created(in Session2Token sessionToken);
-    void notifySession2Destroyed(in Session2Token sessionToken);
     List<MediaSession.Token> getSessions(in ComponentName compName, int userId);
     List<Session2Token> getSession2Tokens(int userId);
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
diff --git a/media/apex/java/android/media/session/MediaController.aidl b/media/java/android/media/session/MediaController.aidl
similarity index 100%
rename from media/apex/java/android/media/session/MediaController.aidl
rename to media/java/android/media/session/MediaController.aidl
diff --git a/media/apex/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
similarity index 99%
rename from media/apex/java/android/media/session/MediaController.java
rename to media/java/android/media/session/MediaController.java
index 1333ab0..6e2c8c5 100644
--- a/media/apex/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -413,6 +413,7 @@
      * Get the session's tag for debugging purposes.
      *
      * @return The session's tag.
+     * @hide
      */
     public String getTag() {
         if (mTag == null) {
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index ca3346c..1b9ebda 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.PendingIntent;
@@ -142,10 +141,9 @@
         MediaSessionManager manager = (MediaSessionManager) context
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         try {
-            MediaSessionEngine.CallbackStub cbStub = new MediaSessionEngine.CallbackStub();
-            SessionCallbackLink cbLink = new SessionCallbackLink(context, cbStub);
+            SessionCallbackLink cbLink = new SessionCallbackLink(context);
             SessionLink sessionLink = manager.createSession(cbLink, tag);
-            mImpl = new MediaSessionEngine(context, sessionLink, cbLink, cbStub);
+            mImpl = new MediaSessionEngine(context, sessionLink, cbLink);
             mMaxBitmapSize = context.getResources().getDimensionPixelSize(
                     android.R.dimen.config_mediaMetadataBitmapMaxSize);
         } catch (RuntimeException e) {
@@ -488,7 +486,6 @@
          * Gets the controller link in this token.
          * @hide
          */
-        @SystemApi
         public ControllerLink getControllerLink() {
             return mControllerLink;
         }
@@ -697,7 +694,6 @@
         /**
          * @hide
          */
-        @SystemApi
         public void onSetMediaButtonEventDelegate(
                 @NonNull MediaSessionEngine.MediaButtonEventDelegate delegate) {
             mMediaButtonEventDelegate = delegate;
diff --git a/media/apex/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java
similarity index 78%
rename from media/apex/java/android/media/session/MediaSessionEngine.java
rename to media/java/android/media/session/MediaSessionEngine.java
index 31714e1..e19bdbc 100644
--- a/media/apex/java/android/media/session/MediaSessionEngine.java
+++ b/media/java/android/media/session/MediaSessionEngine.java
@@ -18,8 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -44,14 +42,12 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
-import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Objects;
 
 /**
  * @hide
  */
-@SystemApi
 public final class MediaSessionEngine implements AutoCloseable {
     private static final String TAG = "MediaSession";
 
@@ -60,10 +56,7 @@
     private final MediaSession.Token mSessionToken;
     private final MediaController mController;
     private final SessionLink mSessionLink;
-    private final SessionCallbackLink mCbLink;
 
-    // Do not change the name of mCallbackWrapper. Support lib accesses this by using reflection.
-    @UnsupportedAppUsage
     private CallbackMessageHandler mCallbackHandler;
     private VolumeProvider mVolumeProvider;
     private PlaybackState mPlaybackState;
@@ -78,14 +71,12 @@
      *
      * @param context The context to use to create the session.
      * @param sessionLink A session link for the binder of MediaSessionRecord
-     * @param cbStub A callback link that handles incoming command to {@link MediaSession.Callback}.
      */
     public MediaSessionEngine(@NonNull Context context, @NonNull SessionLink sessionLink,
-            @NonNull SessionCallbackLink cbLink, @NonNull CallbackStub cbStub) {
+            @NonNull SessionCallbackLink cbLink) {
         mSessionLink = sessionLink;
-        mCbLink = cbLink;
 
-        cbStub.setSessionImpl(this);
+        cbLink.setSessionEngine(this);
         mSessionToken = new MediaSession.Token(mSessionLink.getController());
         mController = new MediaController(context, mSessionToken);
     }
@@ -479,97 +470,97 @@
         }
     }
 
-    private void dispatchPrepare(RemoteUserInfo caller) {
+    void dispatchPrepare(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null);
     }
 
-    private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+    void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
     }
 
-    private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+    void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras);
     }
 
-    private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+    void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
     }
 
-    private void dispatchPlay(RemoteUserInfo caller) {
+    void dispatchPlay(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null);
     }
 
-    private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+    void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
     }
 
-    private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+    void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
     }
 
-    private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+    void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
         postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
     }
 
-    private void dispatchSkipToItem(RemoteUserInfo caller, long id) {
+    void dispatchSkipToItem(RemoteUserInfo caller, long id) {
         postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null);
     }
 
-    private void dispatchPause(RemoteUserInfo caller) {
+    void dispatchPause(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null);
     }
 
-    private void dispatchStop(RemoteUserInfo caller) {
+    void dispatchStop(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null);
     }
 
-    private void dispatchNext(RemoteUserInfo caller) {
+    void dispatchNext(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null);
     }
 
-    private void dispatchPrevious(RemoteUserInfo caller) {
+    void dispatchPrevious(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null);
     }
 
-    private void dispatchFastForward(RemoteUserInfo caller) {
+    void dispatchFastForward(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null);
     }
 
-    private void dispatchRewind(RemoteUserInfo caller) {
+    void dispatchRewind(RemoteUserInfo caller) {
         postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null);
     }
 
-    private void dispatchSeekTo(RemoteUserInfo caller, long pos) {
+    void dispatchSeekTo(RemoteUserInfo caller, long pos) {
         postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null);
     }
 
-    private void dispatchRate(RemoteUserInfo caller, Rating rating) {
+    void dispatchRate(RemoteUserInfo caller, Rating rating) {
         postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
     }
 
-    private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
+    void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
         postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
     }
 
-    private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
+    void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
         postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
     }
 
-    private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
+    void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
             long delay) {
         postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
                 mediaButtonIntent, null, delay);
     }
 
-    private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
+    void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
         postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null);
     }
 
-    private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
+    void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
         postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null);
     }
 
-    private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
+    void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
             ResultReceiver resultCb) {
         Command cmd = new Command(command, args, resultCb);
         postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null);
@@ -979,259 +970,7 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @SystemApi
-    public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
-        private WeakReference<MediaSessionEngine> mSessionImpl;
-
-        private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid) {
-            return new RemoteUserInfo(packageName, pid, uid);
-        }
-
-        public CallbackStub() {
-        }
-
-        @Override
-        public void onCommand(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchCommand(createRemoteUserInfo(packageName, pid, uid),
-                        command, args, cb);
-            }
-        }
-
-        @Override
-        public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
-                int sequenceNumber, ResultReceiver cb) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            try {
-                if (sessionImpl != null) {
-                    sessionImpl.dispatchMediaButton(
-                            createRemoteUserInfo(packageName, pid, uid), mediaButtonIntent);
-                }
-            } finally {
-                if (cb != null) {
-                    cb.send(sequenceNumber, null);
-                }
-            }
-        }
-
-        @Override
-        public void onMediaButtonFromController(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, Intent mediaButtonIntent) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid),
-                        mediaButtonIntent);
-            }
-        }
-
-        @Override
-        public void onPrepare(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onPrepareFromMediaId(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String mediaId,
-                Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPrepareFromMediaId(
-                        createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
-            }
-        }
-
-        @Override
-        public void onPrepareFromSearch(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String query,
-                Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPrepareFromSearch(
-                        createRemoteUserInfo(packageName, pid, uid), query, extras);
-            }
-        }
-
-        @Override
-        public void onPrepareFromUri(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, Uri uri, Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPrepareFromUri(
-                        createRemoteUserInfo(packageName, pid, uid), uri, extras);
-            }
-        }
-
-        @Override
-        public void onPlay(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPlay(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onPlayFromMediaId(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String mediaId,
-                Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPlayFromMediaId(
-                        createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
-            }
-        }
-
-        @Override
-        public void onPlayFromSearch(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String query,
-                Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPlayFromSearch(
-                        createRemoteUserInfo(packageName, pid, uid), query, extras);
-            }
-        }
-
-        @Override
-        public void onPlayFromUri(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, Uri uri, Bundle extras) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPlayFromUri(
-                        createRemoteUserInfo(packageName, pid, uid), uri, extras);
-            }
-        }
-
-        @Override
-        public void onSkipToTrack(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, long id) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchSkipToItem(
-                        createRemoteUserInfo(packageName, pid, uid), id);
-            }
-        }
-
-        @Override
-        public void onPause(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPause(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onStop(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchStop(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onNext(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchNext(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onPrevious(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onFastForward(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchFastForward(
-                        createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onRewind(String packageName, int pid, int uid,
-                ControllerCallbackLink caller) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchRewind(createRemoteUserInfo(packageName, pid, uid));
-            }
-        }
-
-        @Override
-        public void onSeekTo(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, long pos) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchSeekTo(
-                        createRemoteUserInfo(packageName, pid, uid), pos);
-            }
-        }
-
-        @Override
-        public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
-                Rating rating) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchRate(
-                        createRemoteUserInfo(packageName, pid, uid), rating);
-            }
-        }
-
-        @Override
-        public void onCustomAction(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, String action, Bundle args) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchCustomAction(
-                        createRemoteUserInfo(packageName, pid, uid), action, args);
-            }
-        }
-
-        @Override
-        public void onAdjustVolume(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, int direction) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchAdjustVolume(
-                        createRemoteUserInfo(packageName, pid, uid), direction);
-            }
-        }
-
-        @Override
-        public void onSetVolumeTo(String packageName, int pid, int uid,
-                ControllerCallbackLink caller, int value) {
-            MediaSessionEngine sessionImpl = mSessionImpl.get();
-            if (sessionImpl != null) {
-                sessionImpl.dispatchSetVolumeTo(
-                        createRemoteUserInfo(packageName, pid, uid), value);
-            }
-        }
-
-        void setSessionImpl(MediaSessionEngine sessionImpl) {
-            mSessionImpl = new WeakReference<>(sessionImpl);
-        }
-    }
-
-    /**
+     /**
      * A single item that is part of the play queue. It contains a description
      * of the item and its id in the queue.
      */
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index bd8eadd..46f6c71 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
 import android.media.Session2Token;
 import android.os.Handler;
 import android.os.IBinder;
@@ -114,11 +115,11 @@
     }
 
     /**
-     * Notifies that a new MediaSession2 with type {@link Session2Token#TYPE_SESSION} is
+     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
      * created.
      * <p>
      * Do not use this API directly, but create a new instance through the
-     * MediaSession2.Builder instead.
+     * {@link MediaSession2.Builder} instead.
      *
      * @param token newly created session2 token
      */
@@ -129,9 +130,6 @@
         if (token.getType() != Session2Token.TYPE_SESSION) {
             throw new IllegalArgumentException("token's type should be TYPE_SESSION");
         }
-        if (token.isDestroyed()) {
-            throw new IllegalArgumentException("token is already destroyed");
-        }
         try {
             mService.notifySession2Created(token);
         } catch (RemoteException e) {
@@ -140,31 +138,6 @@
     }
 
     /**
-     * Notifies that a new MediaSession2 with type {@link Session2Token#TYPE_SESSION} is
-     * destroyed.
-     * <p>
-     * Do not use this API directly, but close a session with MediaSession2#close() instead.
-     *
-     * @param token destroyed session2 token
-     */
-    public void notifySession2Destroyed(@NonNull Session2Token token) {
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        if (token.getType() != Session2Token.TYPE_SESSION) {
-            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
-        }
-        if (!token.isDestroyed()) {
-            throw new IllegalArgumentException("token should have been destroyed");
-        }
-        try {
-            mService.notifySession2Destroyed(token);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Get a list of controllers for all ongoing sessions. The controllers will
      * be provided in priority order with the most important controller at index
      * 0.
@@ -219,7 +192,7 @@
      * current user.
      * <p>
      * Although this API can be used without any restriction, each session owners can accept or
-     * reject your uses of MediaSession2.
+     * reject your uses of {@link MediaSession2}.
      *
      * @return A list of {@link Session2Token}.
      */
diff --git a/media/apex/java/android/media/session/MediaSessionProviderService.java b/media/java/android/media/session/MediaSessionProviderService.java
similarity index 100%
rename from media/apex/java/android/media/session/MediaSessionProviderService.java
rename to media/java/android/media/session/MediaSessionProviderService.java
diff --git a/media/apex/java/android/media/session/PlaybackState.aidl b/media/java/android/media/session/PlaybackState.aidl
similarity index 100%
rename from media/apex/java/android/media/session/PlaybackState.aidl
rename to media/java/android/media/session/PlaybackState.aidl
diff --git a/media/apex/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
similarity index 100%
rename from media/apex/java/android/media/session/PlaybackState.java
rename to media/java/android/media/session/PlaybackState.java
diff --git a/media/apex/java/android/media/session/SessionCallbackLink.aidl b/media/java/android/media/session/SessionCallbackLink.aidl
similarity index 100%
rename from media/apex/java/android/media/session/SessionCallbackLink.aidl
rename to media/java/android/media/session/SessionCallbackLink.aidl
diff --git a/media/apex/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
similarity index 79%
rename from media/apex/java/android/media/session/SessionCallbackLink.java
rename to media/java/android/media/session/SessionCallbackLink.java
index 3bcb65c..f59a69d 100644
--- a/media/apex/java/android/media/session/SessionCallbackLink.java
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -20,11 +20,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.Rating;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -35,23 +35,23 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Handles incoming commands to {@link MediaSession.Callback}.
  * @hide
  */
-@SystemApi
 public final class SessionCallbackLink implements Parcelable {
     final Context mContext;
-    final CallbackStub mCallbackStub;
     final ISessionCallback mISessionCallback;
 
     /**
      * Constructor for stub (Callee)
+     * @hide
      */
-    SessionCallbackLink(@NonNull Context context, @NonNull CallbackStub callbackStub) {
+    public SessionCallbackLink(@NonNull Context context) {
         mContext = context;
-        mCallbackStub = callbackStub;
-        mISessionCallback = new CallbackStubProxy();
+        mISessionCallback = new CallbackStub();
     }
 
     /**
@@ -59,11 +59,19 @@
      */
     public SessionCallbackLink(IBinder binder) {
         mContext = null;
-        mCallbackStub = null;
         mISessionCallback = ISessionCallback.Stub.asInterface(binder);
     }
 
     /**
+     * Set {@link MediaSessionEngine} which will be used by {@link CallbackStub}.
+     */
+    void setSessionEngine(@Nullable MediaSessionEngine sessionImpl) {
+        if (mISessionCallback instanceof CallbackStub) {
+            ((CallbackStub) mISessionCallback).mSessionImpl = new WeakReference<>(sessionImpl);
+        }
+    }
+
+    /**
      * Notify session that a controller sends a command.
      *
      * @param packageName the package name of the controller
@@ -540,139 +548,24 @@
                 }
             };
 
-    /**
-     * Class for Stub implementation
-     */
-    abstract static class CallbackStub {
-        /** Stub method for ISessionCallback.notifyCommand */
-        public void onCommand(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull String command,
-                @Nullable Bundle args, @Nullable ResultReceiver cb) {
+    private class CallbackStub extends ISessionCallback.Stub {
+        private WeakReference<MediaSessionEngine> mSessionImpl;
+
+        private RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid) {
+            return new RemoteUserInfo(packageName, pid, uid);
         }
 
-        /** Stub method for ISessionCallback.notifyMediaButton */
-        public void onMediaButton(@NonNull String packageName, int pid, int uid,
-                @NonNull Intent mediaButtonIntent, int sequenceNumber,
-                @Nullable ResultReceiver cb) {
-        }
-
-        /** Stub method for ISessionCallback.notifyMediaButtonFromController */
-        public void onMediaButtonFromController(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull Intent mediaButtonIntent) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPrepare */
-        public void onPrepare(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPrepareFromMediaId */
-        public void onPrepareFromMediaId(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
-                @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPrepareFromSearch */
-        public void onPrepareFromSearch(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPrepareFromUri */
-        public void onPrepareFromUri(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPlay */
-        public void onPlay(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPlayFromMediaId */
-        public void onPlayFromMediaId(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
-                @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPlayFromSearch */
-        public void onPlayFromSearch(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPlayFromUri */
-        public void onPlayFromUri(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
-        }
-
-        /** Stub method for ISessionCallback.notifySkipToTrack */
-        public void onSkipToTrack(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, long id) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPause */
-        public void onPause(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyStop */
-        public void onStop(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyNext */
-        public void onNext(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyPrevious */
-        public void onPrevious(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyFastForward */
-        public void onFastForward(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifyRewind */
-        public void onRewind(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller) {
-        }
-
-        /** Stub method for ISessionCallback.notifySeekTo */
-        public void onSeekTo(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, long pos) {
-        }
-
-        /** Stub method for ISessionCallback.notifyRate */
-        public void onRate(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull Rating rating) {
-        }
-
-        /** Stub method for ISessionCallback.notifyCustomAction */
-        public void onCustomAction(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, @NonNull String action,
-                @Nullable Bundle args) {
-        }
-
-        /** Stub method for ISessionCallback.notifyAdjustVolume */
-        public void onAdjustVolume(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, int direction) {
-        }
-
-        /** Stub method for ISessionCallback.notifySetVolumeTo */
-        public void onSetVolumeTo(@NonNull String packageName, int pid, int uid,
-                @NonNull ControllerCallbackLink caller, int value) {
-        }
-    }
-
-    private class CallbackStubProxy extends ISessionCallback.Stub {
         @Override
         public void notifyCommand(String packageName, int pid, int uid,
                 ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onCommand(packageName, pid, uid, caller, command, args, cb);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchCommand(createRemoteUserInfo(packageName, pid, uid),
+                            command, args, cb);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -684,9 +577,15 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onMediaButton(packageName, pid, uid, mediaButtonIntent,
-                        sequenceNumber, cb);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchMediaButton(
+                            createRemoteUserInfo(packageName, pid, uid), mediaButtonIntent);
+                }
             } finally {
+                if (cb != null) {
+                    cb.send(sequenceNumber, null);
+                }
                 Binder.restoreCallingIdentity(token);
             }
         }
@@ -697,8 +596,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onMediaButtonFromController(packageName, pid, uid, caller,
-                        mediaButtonIntent);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid),
+                            mediaButtonIntent);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -710,7 +612,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPrepare(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -722,7 +627,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPrepareFromMediaId(
+                            createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -734,7 +643,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPrepareFromSearch(
+                            createRemoteUserInfo(packageName, pid, uid), query, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -746,7 +659,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPrepareFromUri(
+                            createRemoteUserInfo(packageName, pid, uid), uri, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -758,7 +675,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPlay(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPlay(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -770,7 +690,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPlayFromMediaId(
+                            createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -782,7 +706,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPlayFromSearch(
+                            createRemoteUserInfo(packageName, pid, uid), query, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -794,7 +722,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPlayFromUri(
+                            createRemoteUserInfo(packageName, pid, uid), uri, extras);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -806,7 +738,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onSkipToTrack(packageName, pid, uid, caller, id);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchSkipToItem(
+                            createRemoteUserInfo(packageName, pid, uid), id);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -818,7 +754,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPause(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPause(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -830,7 +769,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onStop(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchStop(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -842,7 +784,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onNext(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchNext(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -854,7 +799,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onPrevious(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -866,7 +814,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onFastForward(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchFastForward(
+                            createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -878,7 +830,10 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onRewind(packageName, pid, uid, caller);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchRewind(createRemoteUserInfo(packageName, pid, uid));
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -890,7 +845,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onSeekTo(packageName, pid, uid, caller, pos);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchSeekTo(
+                            createRemoteUserInfo(packageName, pid, uid), pos);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -902,7 +861,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onRate(packageName, pid, uid, caller, rating);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchRate(
+                            createRemoteUserInfo(packageName, pid, uid), rating);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -913,7 +876,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onCustomAction(packageName, pid, uid, caller, action, args);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchCustomAction(
+                            createRemoteUserInfo(packageName, pid, uid), action, args);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -925,7 +892,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onAdjustVolume(packageName, pid, uid, caller, direction);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchAdjustVolume(
+                            createRemoteUserInfo(packageName, pid, uid), direction);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -937,7 +908,11 @@
             ensureMediaControlPermission();
             final long token = Binder.clearCallingIdentity();
             try {
-                mCallbackStub.onSetVolumeTo(packageName, pid, uid, caller, value);
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchSetVolumeTo(
+                            createRemoteUserInfo(packageName, pid, uid), value);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/media/apex/java/android/media/session/SessionLink.aidl b/media/java/android/media/session/SessionLink.aidl
similarity index 100%
rename from media/apex/java/android/media/session/SessionLink.aidl
rename to media/java/android/media/session/SessionLink.aidl
diff --git a/media/apex/java/android/media/session/SessionLink.java b/media/java/android/media/session/SessionLink.java
similarity index 99%
rename from media/apex/java/android/media/session/SessionLink.java
rename to media/java/android/media/session/SessionLink.java
index 4ea7623..2331a4a 100644
--- a/media/apex/java/android/media/session/SessionLink.java
+++ b/media/java/android/media/session/SessionLink.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata;
@@ -36,7 +35,6 @@
  * Handles incoming commands from {@link MediaSession}.
  * @hide
  */
-@SystemApi
 public final class SessionLink implements Parcelable {
     public static final Parcelable.Creator<SessionLink> CREATOR =
             new Parcelable.Creator<SessionLink>() {
diff --git a/media/apex/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
similarity index 100%
rename from media/apex/java/android/service/media/IMediaBrowserService.aidl
rename to media/java/android/service/media/IMediaBrowserService.aidl
diff --git a/media/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
similarity index 100%
rename from media/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
rename to media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
diff --git a/media/apex/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
similarity index 100%
rename from media/apex/java/android/service/media/MediaBrowserService.java
rename to media/java/android/service/media/MediaBrowserService.java
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 866325c..d7ab854 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -175,7 +175,7 @@
 
 struct OfflineLicenseState {
     jint kOfflineLicenseStateUsable;
-    jint kOfflineLicenseStateInactive;
+    jint kOfflineLicenseStateReleased;
     jint kOfflineLicenseStateUnknown;
 } gOfflineLicenseStates;
 
@@ -797,10 +797,10 @@
     GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
     gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
 
-    GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_USABLE", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_USABLE", "I");
     gOfflineLicenseStates.kOfflineLicenseStateUsable = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_INACTIVE", "I");
-    gOfflineLicenseStates.kOfflineLicenseStateInactive = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_RELEASED", "I");
+    gOfflineLicenseStates.kOfflineLicenseStateReleased = env->GetStaticIntField(clazz, field);
     GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_UNKNOWN", "I");
     gOfflineLicenseStates.kOfflineLicenseStateUnknown = env->GetStaticIntField(clazz, field);
 
@@ -1581,8 +1581,8 @@
     switch(state) {
     case DrmPlugin::kOfflineLicenseStateUsable:
         return gOfflineLicenseStates.kOfflineLicenseStateUsable;
-    case DrmPlugin::kOfflineLicenseStateInactive:
-        return gOfflineLicenseStates.kOfflineLicenseStateInactive;
+    case DrmPlugin::kOfflineLicenseStateReleased:
+        return gOfflineLicenseStates.kOfflineLicenseStateReleased;
     default:
         return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
     }
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index f3442f4..c6b171b 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -913,13 +913,9 @@
     }
 
     // build and return the Bundle
-    MediaAnalyticsItem *item = new MediaAnalyticsItem;
+    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
     item->readFromParcel(reply);
-    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
-
-    // housekeeping
-    delete item;
-    item = NULL;
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
     return mybundle;
 }
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 76bbce7..35b1081 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -682,13 +682,9 @@
         return (jobject) NULL;
     }
 
-    MediaAnalyticsItem *item = new MediaAnalyticsItem;
+    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
     item->readFromParcel(p);
-    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
-
-    // housekeeping
-    delete item;
-    item = NULL;
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
     return mybundle;
 }
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index ca30f32..a7c0159 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -664,15 +664,11 @@
     }
 
     // build and return the Bundle
-    MediaAnalyticsItem *item = new MediaAnalyticsItem;
+    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
     item->readFromParcel(reply);
-    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
-    // housekeeping
-    delete item;
-    item = NULL;
     return mybundle;
-
 }
 
 static jboolean
diff --git a/media/native/midi/include/Doxyfile b/media/native/midi/include/Doxyfile
new file mode 100644
index 0000000..2828f48
--- /dev/null
+++ b/media/native/midi/include/Doxyfile
@@ -0,0 +1,2494 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "Android Native MIDI API (AMidi)"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =" Android Native MIDI API (AMidi)"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = "midi.h"
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/media/native/midi/include/Doxyfile.orig b/media/native/midi/include/Doxyfile.orig
new file mode 100644
index 0000000..e56fb18
--- /dev/null
+++ b/media/native/midi/include/Doxyfile.orig
@@ -0,0 +1,2494 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "My Project"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 6deb47f..e43a60c 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -177,9 +177,6 @@
         webview_functor_callbacks.vk.initialize = &initializeVk;
         webview_functor_callbacks.vk.draw = &drawVk;
         webview_functor_callbacks.vk.postDraw = &postDrawVk;
-        // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
-        // no longer uses GL interop.
-        webview_functor_callbacks.gles.draw = &draw_gl;
         break;
     }
     callbacks_initialized = true;
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
new file mode 100644
index 0000000..7acdfa1
--- /dev/null
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+    name: "CaptivePortalLogin",
+    srcs: ["src/**/*.java"],
+    sdk_version: "system_current",
+    certificate: "platform",
+    static_libs: [
+        "androidx.legacy_legacy-support-v4",
+        "metrics-constants-protos",
+    ],
+    manifest: "AndroidManifest.xml",
+}
diff --git a/packages/CaptivePortalLogin/Android.mk b/packages/CaptivePortalLogin/Android.mk
deleted file mode 100644
index 8c63f45..0000000
--- a/packages/CaptivePortalLogin/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CaptivePortalLogin
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 5ab6632..e15dca0 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -21,9 +21,10 @@
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+    <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
 
     <application android:label="@string/app_name"
                  android:usesCleartextTraffic="true"
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
deleted file mode 100644
index d460041..0000000
--- a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ssl_error_msg"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:textAppearance="?android:attr/textAppearanceSmall"
-    android:layout_marginStart="20dip"
-    android:layout_marginEnd="20dip"
-    android:gravity="center_vertical"
-    android:layout_marginBottom="4dip"
-    android:layout_marginTop="4dip" />
-
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
index ffd57a4..ce05e78 100644
--- a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
+++ b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
@@ -78,7 +78,18 @@
             android:id="@+id/certificate_layout"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:orientation="vertical"
             android:layout_marginBottom="16dip" >
+            <TextView
+                android:id="@+id/ssl_error_msg"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:layout_marginStart="20dip"
+                android:layout_marginEnd="20dip"
+                android:gravity="center_vertical"
+                android:layout_marginBottom="4dip"
+                android:layout_marginTop="16dip" />
         </LinearLayout>
 
     </ScrollView>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0a571c5..ce627ce 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -20,7 +20,7 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.LoadedApk;
+import android.app.Application;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -37,8 +37,9 @@
 import android.net.http.SslCertificate;
 import android.net.http.SslError;
 import android.net.wifi.WifiInfo;
-import android.os.Build;
+import android.net.wifi.WifiManager;
 import android.os.Bundle;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -96,6 +97,7 @@
     private CaptivePortal mCaptivePortal;
     private NetworkCallback mNetworkCallback;
     private ConnectivityManager mCm;
+    private WifiManager mWifiManager;
     private boolean mLaunchBrowser = false;
     private MyWebViewClient mWebViewClient;
     private SwipeRefreshLayout mSwipeRefreshLayout;
@@ -109,7 +111,8 @@
         mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
         logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
 
-        mCm = ConnectivityManager.from(this);
+        mCm = getSystemService(ConnectivityManager.class);
+        mWifiManager = getSystemService(WifiManager.class);
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
         mUserAgent =
                 getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
@@ -151,7 +154,6 @@
         // Also initializes proxy system properties.
         mNetwork = mNetwork.getPrivateDnsBypassingCopy();
         mCm.bindProcessToNetwork(mNetwork);
-        mCm.setProcessDefaultNetworkForHostResolution(mNetwork);
 
         // Proxy system properties must be initialized before setContentView is called because
         // setContentView initializes the WebView logic which in turn reads the system properties.
@@ -190,9 +192,12 @@
 
     // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
     private void setWebViewProxy() {
-        LoadedApk loadedApk = getApplication().mLoadedApk;
+        // TODO: migrate to androidx WebView proxy setting API as soon as it is finalized
         try {
-            Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
+            final Field loadedApkField = Application.class.getDeclaredField("mLoadedApk");
+            final Class<?> loadedApkClass = loadedApkField.getType();
+            final Object loadedApk = loadedApkField.get(getApplication());
+            Field receiversField = loadedApkClass.getDeclaredField("mReceivers");
             receiversField.setAccessible(true);
             ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
             for (Object receiverMap : receivers.values()) {
@@ -333,7 +338,11 @@
 
     private static String sanitizeURL(URL url) {
         // In non-Debug build, only show host to avoid leaking private info.
-        return Build.IS_DEBUGGABLE ? Objects.toString(url) : host(url);
+        return isDebuggable() ? Objects.toString(url) : host(url);
+    }
+
+    private static boolean isDebuggable() {
+        return SystemProperties.getInt("ro.debuggable", 0) == 1;
     }
 
     private void testForCaptivePortal() {
@@ -586,19 +595,18 @@
         }
 
         private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
+            ((TextView) certificateLayout.findViewById(R.id.ssl_error_msg))
+                    .setText(sslErrorMessage(error));
             SslCertificate cert = error.getCertificate();
-
-            View certificateView = cert.inflateCertificateView(CaptivePortalLoginActivity.this);
-            final LinearLayout placeholder = (LinearLayout) certificateView
-                    .findViewById(com.android.internal.R.id.placeholder);
-            LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
-
-            TextView textView = (TextView) factory.inflate(
-                    R.layout.ssl_error_msg, placeholder, false);
-            textView.setText(sslErrorMessage(error));
-            placeholder.addView(textView);
-
-            certificateLayout.addView(certificateView);
+            // TODO: call the method directly once inflateCertificateView is @SystemApi
+            try {
+                final View certificateView = (View) SslCertificate.class.getMethod(
+                        "inflateCertificateView", Context.class)
+                        .invoke(cert, CaptivePortalLoginActivity.this);
+                certificateLayout.addView(certificateView);
+            } catch (ReflectiveOperationException | SecurityException e) {
+                Log.e(TAG, "Could not create certificate view", e);
+            }
         }
     }
 
@@ -619,11 +627,30 @@
 
     private String getHeaderTitle() {
         NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
-        if (nc == null || TextUtils.isEmpty(nc.getSSID())
-            || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+        final String ssid = getSsid();
+        if (TextUtils.isEmpty(ssid)
+                || nc == null || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
             return getString(R.string.action_bar_label);
         }
-        return getString(R.string.action_bar_title, WifiInfo.removeDoubleQuotes(nc.getSSID()));
+        return getString(R.string.action_bar_title, ssid);
+    }
+
+    // TODO: remove once SSID is obtained from NetworkCapabilities
+    private String getSsid() {
+        if (mWifiManager == null) {
+            return null;
+        }
+        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        return removeDoubleQuotes(wifiInfo.getSSID());
+    }
+
+    private static String removeDoubleQuotes(String string) {
+        if (string == null) return null;
+        final int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
     }
 
     private String getHeaderSubtitle(URL url) {
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index 777283d..be2cb0d 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -23,7 +23,7 @@
     <!-- Number of milliseconds user can spend driving with the keyguard up. After that, we switch to Guest. -->
     <!-- If the number is negative, the feature is disabled.
          If it's zero, we switch to guest immediately as we start driving. -->
-    <integer name="driving_on_keyguard_timeout_ms">30000</integer>
+    <integer name="driving_on_keyguard_timeout_ms">-1</integer>
 
     <!--Percentage of the screen height, from the bottom, that a notification panel being
     partially closed at will result in it remaining open if released-->
diff --git a/packages/ExtServices/Android.bp b/packages/ExtServices/Android.bp
new file mode 100644
index 0000000..77972fe
--- /dev/null
+++ b/packages/ExtServices/Android.bp
@@ -0,0 +1,11 @@
+android_library {
+    name: "ExtServices-core",
+     srcs: [
+         "src/**/*.java",
+     ],
+     resource_dirs: [
+         "res",
+     ],
+
+     manifest: "AndroidManifest.xml",
+}
\ No newline at end of file
diff --git a/packages/ExtServices/tests/Android.bp b/packages/ExtServices/tests/Android.bp
new file mode 100644
index 0000000..5de4548
--- /dev/null
+++ b/packages/ExtServices/tests/Android.bp
@@ -0,0 +1,26 @@
+android_test {
+    name: "ExtServicesUnitTests",
+
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+
+    // We only want this apk build for tests.
+    certificate: "platform",
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    static_libs: [
+        "ExtServices-core",
+        "android-support-test",
+        "mockito-target-minus-junit4",
+        "espresso-core",
+        "truth-prebuilt",
+        "testables",
+        "testng",
+    ],
+
+    platform_apis: true,
+}
\ No newline at end of file
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
deleted file mode 100644
index a57fa94..0000000
--- a/packages/ExtServices/tests/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-LOCAL_CERTIFICATE := platform
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    mockito-target-minus-junit4 \
-    espresso-core \
-    truth-prebuilt \
-    testables \
-    testng
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ExtServicesUnitTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_INSTRUMENTATION_FOR := ExtServices
-
-include $(BUILD_PACKAGE)
diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/packages/ExtServices/tests/AndroidManifest.xml
index ddf725b..3cf1527 100644
--- a/packages/ExtServices/tests/AndroidManifest.xml
+++ b/packages/ExtServices/tests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="android.ext.services"
+                     android:targetPackage="android.ext.services.tests.unit"
                      android:label="ExtServices Test Cases">
     </instrumentation>
 
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
index 12908e6..c59885e 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
@@ -180,7 +180,7 @@
         assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM));
 
         when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
-        assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
+        assertEquals(NotificationCategorizer.CATEGORY_HIGH, nc.getCategory(mEntry));
     }
 
     @Test
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index b0522f2..d656593 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -41,4 +41,5 @@
         "NetworkStackLib"
     ],
     manifest: "AndroidManifest.xml",
+    required: ["NetworkStackPermissionStub"],
 }
\ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 5ab833b..ac55bfa 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -25,6 +25,8 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+    <!-- Signature permission defined in NetworkStackStub -->
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
     <!-- Launch captive portal app as specific user -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.NETWORK_STACK" />
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index b34efc4..dbffa6d 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -109,6 +109,8 @@
     private static final boolean DBG  = true;
     private static final boolean VDBG = false;
     private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
+    // TODO: use another permission for CaptivePortalLoginActivity once it has its own certificate
+    private static final String PERMISSION_NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     // Default configuration values for captive portal detection probes.
     // TODO: append a random length parameter to the default HTTPS url.
     // TODO: randomize browser version ids in the default User-Agent String.
@@ -682,7 +684,7 @@
                                 public void appResponse(int response) {
                                     if (response == APP_RETURN_WANTED_AS_IS) {
                                         mContext.enforceCallingPermission(
-                                                android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                                                PERMISSION_NETWORK_SETTINGS,
                                                 "CaptivePortal");
                                     }
                                     sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
@@ -692,7 +694,7 @@
                                 public void logEvent(int eventId, String packageName)
                                         throws RemoteException {
                                     mContext.enforceCallingPermission(
-                                            android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                                            PERMISSION_NETWORK_SETTINGS,
                                             "CaptivePortal");
                                     mCallback.logCaptivePortalLoginEvent(eventId, packageName);
                                 }
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
index 82bf038..f6eb900 100644
--- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -19,6 +19,7 @@
 import static android.os.Binder.getCallingUid;
 
 import android.os.Process;
+import android.os.UserHandle;
 
 /**
  * Utility class to check calling permissions on the network stack.
@@ -32,7 +33,7 @@
     public static void checkNetworkStackCallingPermission() {
         // TODO: check that the calling PID is the system server.
         final int caller = getCallingUid();
-        if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
+        if (caller != Process.SYSTEM_UID && UserHandle.getAppId(caller) != Process.BLUETOOTH_UID) {
             throw new SecurityException("Invalid caller: " + caller);
         }
     }
diff --git a/packages/NetworkStackPermissionStub/Android.bp b/packages/NetworkStackPermissionStub/Android.bp
new file mode 100644
index 0000000..94870c9
--- /dev/null
+++ b/packages/NetworkStackPermissionStub/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 The Android Open 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.
+//
+
+// Stub APK to define permissions for NetworkStack
+android_app {
+    name: "NetworkStackPermissionStub",
+    // TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
+    // a classes.dex.
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+    manifest: "AndroidManifest.xml",
+}
diff --git a/packages/NetworkStackPermissionStub/AndroidManifest.xml b/packages/NetworkStackPermissionStub/AndroidManifest.xml
new file mode 100644
index 0000000..2ccf5ff
--- /dev/null
+++ b/packages/NetworkStackPermissionStub/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open 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.mainline.networkstack.permissionstub">
+    <!--
+    This package only exists to define the below permissions, and enforce that they are only
+    granted to apps sharing the same signature.
+    Permissions defined here are intended to be used only by the NetworkStack: both
+    NetworkStack and this stub APK are to be signed with a dedicated certificate to ensure
+    that, with the below permissions being signature permissions.
+
+    This APK *must* be installed, even if the NetworkStack app is not installed, because otherwise,
+    any application will be able to define this permission and the system will give that application
+    full access to the network stack.
+     -->
+    <permission android:name="android.permission.MAINLINE_NETWORK_STACK"
+                android:protectionLevel="signature"/>
+
+    <application android:name="com.android.server.NetworkStackPermissionStub"/>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java b/packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java
new file mode 100644
index 0000000..01e59d2
--- /dev/null
+++ b/packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.app.Application;
+
+/**
+ * Empty application for NetworkStackStub that only exists because soong builds complain if APKs
+ * have no source file.
+ */
+public class NetworkStackPermissionStub extends Application {
+}
diff --git a/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java b/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java
index 68ba5fb..be3365f 100644
--- a/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java
+++ b/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java
@@ -27,7 +27,6 @@
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.OnLifecycleEvent;
-import androidx.recyclerview.widget.RecyclerView;
 
 /**
  * UI controller that adds a shadow appear/disappear animation to action bar scroll.
@@ -41,40 +40,36 @@
 
     @VisibleForTesting
     ScrollChangeWatcher mScrollChangeWatcher;
-    private RecyclerView mRecyclerView;
+    private View mScrollView;
     private boolean mIsScrollWatcherAttached;
 
     /**
      * Wire up the animation to to an {@link Activity}. Shadow will be applied to activity's
      * action bar.
      */
-    public static ActionBarShadowController attachToRecyclerView(
-            Activity activity, Lifecycle lifecycle, RecyclerView recyclerView) {
-        return new ActionBarShadowController(activity, lifecycle, recyclerView);
+    public static ActionBarShadowController attachToView(
+            Activity activity, Lifecycle lifecycle, View scrollView) {
+        return new ActionBarShadowController(activity, lifecycle, scrollView);
     }
 
     /**
      * Wire up the animation to to a {@link View}. Shadow will be applied to the view.
      */
-    public static ActionBarShadowController attachToRecyclerView(
-            View anchorView, Lifecycle lifecycle, RecyclerView recyclerView) {
-        return new ActionBarShadowController(anchorView, lifecycle, recyclerView);
+    public static ActionBarShadowController attachToView(
+            View anchorView, Lifecycle lifecycle, View scrollView) {
+        return new ActionBarShadowController(anchorView, lifecycle, scrollView);
     }
 
-    private ActionBarShadowController(Activity activity, Lifecycle lifecycle,
-            RecyclerView recyclerView) {
-        mScrollChangeWatcher =
-                new ActionBarShadowController.ScrollChangeWatcher(activity);
-        mRecyclerView = recyclerView;
+    private ActionBarShadowController(Activity activity, Lifecycle lifecycle, View scrollView) {
+        mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(activity);
+        mScrollView = scrollView;
         attachScrollWatcher();
         lifecycle.addObserver(this);
     }
 
-    private ActionBarShadowController(View anchorView, Lifecycle lifecycle,
-            RecyclerView recyclerView) {
-        mScrollChangeWatcher =
-                new ActionBarShadowController.ScrollChangeWatcher(anchorView);
-        mRecyclerView = recyclerView;
+    private ActionBarShadowController(View anchorView, Lifecycle lifecycle, View scrollView) {
+        mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(anchorView);
+        mScrollView = scrollView;
         attachScrollWatcher();
         lifecycle.addObserver(this);
     }
@@ -83,21 +78,21 @@
     private void attachScrollWatcher() {
         if (!mIsScrollWatcherAttached) {
             mIsScrollWatcherAttached = true;
-            mRecyclerView.addOnScrollListener(mScrollChangeWatcher);
-            mScrollChangeWatcher.updateDropShadow(mRecyclerView);
+            mScrollView.setOnScrollChangeListener(mScrollChangeWatcher);
+            mScrollChangeWatcher.updateDropShadow(mScrollView);
         }
     }
 
     @OnLifecycleEvent(ON_STOP)
     private void detachScrollWatcher() {
-        mRecyclerView.removeOnScrollListener(mScrollChangeWatcher);
+        mScrollView.setOnScrollChangeListener(null);
         mIsScrollWatcherAttached = false;
     }
 
     /**
      * Update the drop shadow as the scrollable entity is scrolled.
      */
-    final class ScrollChangeWatcher extends RecyclerView.OnScrollListener {
+    final class ScrollChangeWatcher implements View.OnScrollChangeListener {
 
         private final Activity mActivity;
         private final View mAnchorView;
@@ -112,9 +107,9 @@
             mActivity = null;
         }
 
-        // RecyclerView scrolled.
         @Override
-        public void onScrolled(RecyclerView view, int dx, int dy) {
+        public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX,
+                int oldScrollY) {
             updateDropShadow(view);
         }
 
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index d188c65..d879087 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -4,7 +4,7 @@
 dehboxturtle@google.com
 dhnishi@google.com
 dling@google.com
-dsandler@google.com
+dsandler@android.com
 evanlaird@google.com
 jackqdyulei@google.com
 jmonk@google.com
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ec70f8c..3e904a3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -101,12 +101,10 @@
     <string name="connected_via_passpoint">Connected via %1$s</string>
     <!-- Status message of Wi-Fi when it is connected by a app (via suggestion or network request). [CHAR LIMIT=NONE] -->
     <string name="connected_via_app">Connected via <xliff:g id="name" example="Wifi App">%1$s</xliff:g></string>
-    <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
-    <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string>
     <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
     <string name="available_via_passpoint">Available via %1$s</string>
     <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] -->
-    <string name="tap_to_set_up">Tap to set up</string>
+    <string name="tap_to_sign_up">Tap to sign up</string>
     <!-- Package name for Settings app-->
     <string name="settings_package" translatable="false">com.android.settings</string>
     <!-- Package name for Certinstaller app-->
@@ -129,78 +127,16 @@
     <!-- Status message of Wi-Fi when an available network is a carrier network. [CHAR LIMIT=NONE] -->
     <string name="available_via_carrier">Available via %1$s</string>
 
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_AP_CONNECTION. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_ap_connection">Connection failed</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_URL_INVALID. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_server_url_invalid">Invalid OSU server URL</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_CONNECTION. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_server_connection">OSU server connection failed</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_VALIDATION. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_server_validation">OSU server validation failed</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_service_provider_verification">Invalid OSU server certificate</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_ABORTED. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_provisioning_aborted">Provisioning aborted</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_NOT_AVAILABLE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_provisioning_not_available">Provisioning not available</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_INVALID_SERVER_URL. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_invalid_server_url">Invalid OSU server URL</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_COMMAND_TYPE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_unexpected_command_type">Unexpected command type</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_unexpected_soap_message_type">Unexpected SOAP message type</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SOAP_MESSAGE_EXCHANGE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_soap_message_exchange">SOAP message exchange failed</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_START_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_start_redirect_listener">Redirect listener failed to start</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_timed_out_redirect_listener">Timed out waiting for redirect</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_OSU_ACTIVITY_FOUND. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_osu_activity_found">No OSU activity found</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_unexpected_soap_message_status">Unexpected SOAP message status</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_PPS_MO. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_pps_mo">Failed to find PPS-MO</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_aaa_server_trust_root_node">Failed to find trust root node for AAA server</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_remediation_server_trust_root_node">Failed to find trust root node for remediation server</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_policy_server_trust_root_node">Failed to find trust root node for policy server</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_retrieve_trust_root_certificates">Failed to retrieve trust root certificates</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_no_aaa_trust_root_certificate">Failed to find trust root certificate for AAA server</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_add_passpoint_configuration">Failed to add PassPoint configuration</string>
-    <!-- Status message of OSU Provider on receiving OSU_FAILURE_OSU_PROVIDER_NOT_FOUND. [CHAR LIMIT=NONE] -->
-    <string name="osu_failure_osu_provider_not_found">Failed to find an OSU provider</string>
-
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTING. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_ap_connecting">Connecting</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTED. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_ap_connected">Connected</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTING. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_server_connecting">Connecting to OSU server</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_VALIDATED. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_server_validated">OSU server validated</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTED. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_server_connected">Connected to OSU server</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_INIT_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_init_soap_exchange">Initial SOAP exchange</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_waiting_for_redirect_response">Waiting for redirect response</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_REDIRECT_RESPONSE_RECEIVED. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_redirect_response_received">Received redirect response</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_SECOND_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_second_soap_exchange">Second SOAP exchange</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_THIRD_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_third_soap_exchange">Third SOAP exchange</string>
-    <!-- Status message of OSU Provider on receiving OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS. [CHAR LIMIT=NONE] -->
-    <string name="osu_status_retrieving_trust_root_certs">Retrieving trust root certificates</string>
-
+    <!-- Status message of OSU Provider upon initiating provisioning flow [CHAR LIMIT=NONE] -->
+    <string name="osu_opening_provider">Opening <xliff:g id="passpointProvider" example="Passpoint Provider">%1$s</xliff:g></string>
+    <!-- Status message of OSU Provider when connection fails [CHAR LIMIT=NONE] -->
+    <string name="osu_connect_failed">Couldn\u2019t connect</string>
+    <!-- Status message of OSU Provider after user completes provisioning flow [CHAR LIMIT=NONE] -->
+    <string name="osu_completing_sign_up">Completing sign-up\u2026</string>
+    <!-- Status message of OSU Provider when sign up could not be completed [CHAR LIMIT=NONE] -->
+    <string name="osu_sign_up_failed">Couldn\u2019t complete sign-up. Tap to try again.</string>
     <!-- Status message of OSU Provider on completing provisioning. [CHAR LIMIT=NONE] -->
-    <string name="osu_provisioning_complete">Provisioning complete</string>
+    <string name="osu_sign_up_complete">Sign-up complete. Connecting\u2026</string>
 
     <!-- Speed label for very slow network speed -->
     <string name="speed_label_very_slow">Very Slow</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index c751c39..d32e85f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -56,37 +56,25 @@
 
     public static void updateLocationEnabled(Context context, boolean enabled, int userId,
             int source) {
+        LocationManager locationManager = context.getSystemService(LocationManager.class);
+
         Settings.Secure.putIntForUser(
                 context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source,
                 userId);
-        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
 
-        final int oldMode = Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+        final int oldMode = locationManager.isLocationEnabled()
+                ? Settings.Secure.LOCATION_MODE_ON
+                : Settings.Secure.LOCATION_MODE_OFF;
         final int newMode = enabled
-                ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+                ? Settings.Secure.LOCATION_MODE_ON
                 : Settings.Secure.LOCATION_MODE_OFF;
         intent.putExtra(CURRENT_MODE_KEY, oldMode);
         intent.putExtra(NEW_MODE_KEY, newMode);
         context.sendBroadcastAsUser(
                 intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
-        LocationManager locationManager =
-                (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
-        locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId));
-    }
 
-    public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId,
-            int source) {
-        Settings.Secure.putIntForUser(
-                context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source,
-                userId);
-        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
-        intent.putExtra(CURRENT_MODE_KEY, oldMode);
-        intent.putExtra(NEW_MODE_KEY, newMode);
-        context.sendBroadcastAsUser(
-                intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
-        return Settings.Secure.putIntForUser(
-                context.getContentResolver(), Settings.Secure.LOCATION_MODE, newMode, userId);
+        locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId));
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c74d9ff..ac2c2c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -888,19 +888,20 @@
 
         if (isOsuProvider()) {
             if (mOsuProvisioningComplete) {
-                summary.append(mContext.getString(R.string.osu_provisioning_complete));
+                summary.append(mContext.getString(R.string.osu_sign_up_complete));
             } else if (mOsuFailure != null) {
                 summary.append(mOsuFailure);
             } else if (mOsuStatus != null) {
                 summary.append(mOsuStatus);
             } else {
-                summary.append(mContext.getString(R.string.tap_to_set_up));
+                summary.append(mContext.getString(R.string.tap_to_sign_up));
             }
         } else if (isActive()) {
             if (isPasspoint()) {
                 // This is the active connection on passpoint
-                summary.append(getSummary(mContext, ssid, getDetailedState(),
-                        false, null, mConfig.providerFriendlyName));
+                summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(),
+                        /* isEphemeral */ false,
+                        /* suggestionOrSpecifierPackageName */ null));
             } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED
                     && mIsCarrierAp) {
                 // This is the active connection on a carrier AP
@@ -908,7 +909,7 @@
                         mCarrierName));
             } else {
                 // This is the active connection on non-passpoint network
-                summary.append(getSummary(mContext, getDetailedState(),
+                summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(),
                         mInfo != null && mInfo.isEphemeral(),
                         mInfo != null ? mInfo.getNetworkSuggestionOrSpecifierPackageName() : null));
             }
@@ -1340,14 +1341,9 @@
     }
 
     public static String getSummary(Context context, String ssid, DetailedState state,
-            boolean isEphemeral, String suggestionOrSpecifierPackageName,
-            String passpointProvider) {
+            boolean isEphemeral, String suggestionOrSpecifierPackageName) {
         if (state == DetailedState.CONNECTED) {
-            if (!TextUtils.isEmpty(passpointProvider)) {
-                // Special case for connected + passpoint networks.
-                String format = context.getString(R.string.ssid_by_passpoint_provider);
-                return String.format(format, ssid, passpointProvider);
-            } else if (isEphemeral && !TextUtils.isEmpty(suggestionOrSpecifierPackageName)) {
+            if (isEphemeral && !TextUtils.isEmpty(suggestionOrSpecifierPackageName)) {
                 CharSequence appLabel =
                         getAppLabel(suggestionOrSpecifierPackageName, context.getPackageManager());
                 return context.getString(R.string.connected_via_app, appLabel);
@@ -1401,19 +1397,6 @@
         return String.format(formats[index], ssid);
     }
 
-    public static String getSummary(Context context, DetailedState state, boolean isEphemeral,
-                                    String suggestionOrSpecifierPackageName) {
-        return getSummary(context, null, state, isEphemeral, suggestionOrSpecifierPackageName,
-                null);
-    }
-
-    public static String getSummary(Context context, DetailedState state, boolean isEphemeral,
-                                    String suggestionOrSpecifierPackageName,
-                                    String passpointProvider) {
-        return getSummary(context, null, state, false, suggestionOrSpecifierPackageName,
-                passpointProvider);
-    }
-
     public static String convertToQuotedString(String string) {
         return "\"" + string + "\"";
     }
@@ -1554,91 +1537,12 @@
      * All methods are invoked on the Main Thread
      */
     private class AccessPointProvisioningCallback extends ProvisioningCallback {
-        // TODO: Remove logs and implement summary changing logic for these provisioning callbacks.
         @Override
         @MainThread public void onProvisioningFailure(int status) {
-            switch (status) {
-                case OSU_FAILURE_AP_CONNECTION:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_ap_connection);
-                    break;
-                case OSU_FAILURE_SERVER_URL_INVALID:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_server_url_invalid);
-                    break;
-                case OSU_FAILURE_SERVER_CONNECTION:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_server_connection);
-                    break;
-                case OSU_FAILURE_SERVER_VALIDATION:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_server_validation);
-                    break;
-                case OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_service_provider_verification);
-                    break;
-                case OSU_FAILURE_PROVISIONING_ABORTED:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_provisioning_aborted);
-                    break;
-                case OSU_FAILURE_PROVISIONING_NOT_AVAILABLE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_provisioning_not_available);
-                    break;
-                case OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_invalid_server_url);
-                    break;
-                case OSU_FAILURE_UNEXPECTED_COMMAND_TYPE:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_unexpected_command_type);
-                    break;
-                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_unexpected_soap_message_type);
-                    break;
-                case OSU_FAILURE_SOAP_MESSAGE_EXCHANGE:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_soap_message_exchange);
-                    break;
-                case OSU_FAILURE_START_REDIRECT_LISTENER:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_start_redirect_listener);
-                    break;
-                case OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_timed_out_redirect_listener);
-                    break;
-                case OSU_FAILURE_NO_OSU_ACTIVITY_FOUND:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_no_osu_activity_found);
-                    break;
-                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_unexpected_soap_message_status);
-                    break;
-                case OSU_FAILURE_NO_PPS_MO:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_no_pps_mo);
-                    break;
-                case OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_no_aaa_server_trust_root_node);
-                    break;
-                case OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_no_remediation_server_trust_root_node);
-                    break;
-                case OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_no_policy_server_trust_root_node);
-                    break;
-                case OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_retrieve_trust_root_certificates);
-                    break;
-                case OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_no_aaa_trust_root_certificate);
-                    break;
-                case OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION:
-                    mOsuFailure = mContext.getString(
-                            R.string.osu_failure_add_passpoint_configuration);
-                    break;
-                case OSU_FAILURE_OSU_PROVIDER_NOT_FOUND:
-                    mOsuFailure = mContext.getString(R.string.osu_failure_osu_provider_not_found);
-                    break;
+            if (TextUtils.equals(mOsuStatus, mContext.getString(R.string.osu_completing_sign_up))) {
+                mOsuFailure = mContext.getString(R.string.osu_sign_up_failed);
+            } else {
+                mOsuFailure = mContext.getString(R.string.osu_connect_failed);
             }
             mOsuStatus = null;
             mOsuProvisioningComplete = false;
@@ -1651,50 +1555,37 @@
 
         @Override
         @MainThread public void onProvisioningStatus(int status) {
+            String newStatus = null;
             switch (status) {
                 case OSU_STATUS_AP_CONNECTING:
-                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connecting);
-                    break;
                 case OSU_STATUS_AP_CONNECTED:
-                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connected);
-                    break;
                 case OSU_STATUS_SERVER_CONNECTING:
-                    mOsuStatus = mContext.getString(R.string.osu_status_server_connecting);
-                    break;
                 case OSU_STATUS_SERVER_VALIDATED:
-                    mOsuStatus = mContext.getString(R.string.osu_status_server_validated);
-                    break;
                 case OSU_STATUS_SERVER_CONNECTED:
-                    mOsuStatus = mContext.getString(R.string.osu_status_server_connected);
-                    break;
                 case OSU_STATUS_INIT_SOAP_EXCHANGE:
-                    mOsuStatus = mContext.getString(R.string.osu_status_init_soap_exchange);
-                    break;
                 case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE:
-                    mOsuStatus = mContext.getString(
-                            R.string.osu_status_waiting_for_redirect_response);
+                    newStatus = String.format(mContext.getString(R.string.osu_opening_provider),
+                            mOsuProvider.getFriendlyName());
                     break;
                 case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED:
-                    mOsuStatus = mContext.getString(R.string.osu_status_redirect_response_received);
-                    break;
                 case OSU_STATUS_SECOND_SOAP_EXCHANGE:
-                    mOsuStatus = mContext.getString(R.string.osu_status_second_soap_exchange);
-                    break;
                 case OSU_STATUS_THIRD_SOAP_EXCHANGE:
-                    mOsuStatus = mContext.getString(R.string.osu_status_third_soap_exchange);
-                    break;
                 case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS:
-                    mOsuStatus = mContext.getString(
-                            R.string.osu_status_retrieving_trust_root_certs);
+                    newStatus = mContext.getString(
+                            R.string.osu_completing_sign_up);
                     break;
             }
+            boolean updated = !TextUtils.equals(mOsuStatus, newStatus);
+            mOsuStatus = newStatus;
             mOsuFailure = null;
             mOsuProvisioningComplete = false;
-            ThreadUtils.postOnMainThread(() -> {
-                if (mAccessPointListener != null) {
-                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
-                }
-            });
+            if (updated) {
+                ThreadUtils.postOnMainThread(() -> {
+                    if (mAccessPointListener != null) {
+                        mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                    }
+                });
+            }
         }
 
         @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 92ebe44..4d76e44 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -38,7 +38,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
@@ -86,25 +85,6 @@
     }
 
     @Test
-    public void testUpdateLocationMode_sendBroadcast() {
-        int currentUserId = ActivityManager.getCurrentUser();
-        Utils.updateLocationMode(
-                mContext,
-                Secure.LOCATION_MODE_OFF,
-                Secure.LOCATION_MODE_HIGH_ACCURACY,
-                currentUserId,
-                Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
-
-        verify(mContext).sendBroadcastAsUser(
-                argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
-                ArgumentMatchers.eq(UserHandle.of(currentUserId)),
-                ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
-        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN))
-                .isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
-    }
-
-    @Test
     public void testUpdateLocationEnabled_sendBroadcast() {
         int currentUserId = ActivityManager.getCurrentUser();
         Utils.updateLocationEnabled(mContext, true, currentUserId,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionBarShadowControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionBarShadowControllerTest.java
index a25b512..4e00ab5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionBarShadowControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionBarShadowControllerTest.java
@@ -22,6 +22,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -49,6 +50,8 @@
     @Mock
     private RecyclerView mRecyclerView;
     @Mock
+    private View mScrollView;
+    @Mock
     private Activity mActivity;
     @Mock
     private ActionBar mActionBar;
@@ -66,51 +69,60 @@
     }
 
     @Test
-    public void attachToRecyclerView_shouldAddScrollWatcherAndUpdateActionBar() {
+    public void attachToView_shouldAddScrollWatcherAndUpdateActionBar() {
         when(mRecyclerView.canScrollVertically(-1)).thenReturn(false);
 
-        ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
+        ActionBarShadowController.attachToView(mActivity, mLifecycle, mRecyclerView);
 
         verify(mActionBar).setElevation(ActionBarShadowController.ELEVATION_LOW);
     }
 
     @Test
-    public void attachToRecyclerView_customViewAsActionBar_shouldUpdateElevationOnScroll() {
+    public void attachToView_scrollView_shouldAddScrollWatcherAndUpdateActionBar() {
+        when(mScrollView.canScrollVertically(-1)).thenReturn(false);
+
+        ActionBarShadowController.attachToView(mActivity, mLifecycle, mScrollView);
+
+        verify(mActionBar).setElevation(ActionBarShadowController.ELEVATION_LOW);
+    }
+
+    @Test
+    public void attachToView_customViewAsActionBar_shouldUpdateElevationOnScroll() {
         // Setup
         mView.setElevation(50);
         when(mRecyclerView.canScrollVertically(-1)).thenReturn(false);
         final ActionBarShadowController controller =
-                ActionBarShadowController.attachToRecyclerView(mView, mLifecycle, mRecyclerView);
+                ActionBarShadowController.attachToView(mView, mLifecycle, mRecyclerView);
         assertThat(mView.getElevation()).isEqualTo(ActionBarShadowController.ELEVATION_LOW);
 
         // Scroll
         when(mRecyclerView.canScrollVertically(-1)).thenReturn(true);
-        controller.mScrollChangeWatcher.onScrolled(mRecyclerView, 10 /* dx */, 10 /* dy */);
+        controller.mScrollChangeWatcher.onScrollChange(mRecyclerView, 10, 10, 0, 0);
         assertThat(mView.getElevation()).isEqualTo(ActionBarShadowController.ELEVATION_HIGH);
     }
 
     @Test
-    public void attachToRecyclerView_lifecycleChange_shouldAttachDetach() {
-        ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
+    public void attachToView_lifecycleChange_shouldAttachDetach() {
+        ActionBarShadowController.attachToView(mActivity, mLifecycle, mRecyclerView);
 
-        verify(mRecyclerView).addOnScrollListener(any());
+        verify(mRecyclerView).setOnScrollChangeListener(any());
 
         mLifecycle.handleLifecycleEvent(ON_START);
         mLifecycle.handleLifecycleEvent(ON_STOP);
-        verify(mRecyclerView).removeOnScrollListener(any());
+        verify(mRecyclerView).setOnScrollChangeListener(isNull());
 
         mLifecycle.handleLifecycleEvent(ON_START);
-        verify(mRecyclerView, times(2)).addOnScrollListener(any());
+        verify(mRecyclerView, times(3)).setOnScrollChangeListener(any());
     }
 
     @Test
     public void onScrolled_nullAnchorViewAndActivity_shouldNotCrash() {
         final Activity activity = null;
         final ActionBarShadowController controller =
-                ActionBarShadowController.attachToRecyclerView(activity, mLifecycle, mRecyclerView);
+                ActionBarShadowController.attachToView(activity, mLifecycle, mRecyclerView);
 
         // Scroll
-        controller.mScrollChangeWatcher.onScrolled(mRecyclerView, 10 /* dx */, 10 /* dy */);
+        controller.mScrollChangeWatcher.onScrollChange(mRecyclerView, 10, 10, 0, 0);
         // no crash
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6ca8261..479c964 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2712,6 +2712,9 @@
         dumpSetting(s, p,
                 Settings.System.MUTE_STREAMS_AFFECTED,
                 SystemSettingsProto.Volume.MUTE_STREAMS_AFFECTED);
+        dumpSetting(s, p,
+                Settings.System.MASTER_BALANCE,
+                SystemSettingsProto.Volume.MASTER_BALANCE);
         p.end(volumeToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4453121..23e5f0e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4231,7 +4231,7 @@
 
                         String defLocationMode = Integer.toString(
                                 !TextUtils.isEmpty(locationProvidersAllowed.getValue())
-                                        ? Secure.LOCATION_MODE_HIGH_ACCURACY
+                                        ? Secure.LOCATION_MODE_ON
                                         : Secure.LOCATION_MODE_OFF);
                         secureSettings.insertSettingLocked(
                                 Secure.LOCATION_MODE, defLocationMode,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c3c3f25..c032683 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -145,6 +145,7 @@
     <uses-permission android:name="android.permission.SET_TIME_ZONE" />
     <uses-permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" />
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <!-- Permission needed to rename bugreport notifications (so they're not shown as Shell) -->
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
     <!-- Permission needed to hold a wakelock in dumpstate.cpp (drop_root_user()) -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a18eb76..815ae9a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -22,6 +22,11 @@
         android:sharedUserId="android.uid.systemui"
         coreApp="true">
 
+    <!-- Using OpenGL ES 2.0 -->
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true" />
+
     <!-- SysUI must be the one to define this permission; its name is
          referenced by the core OS. -->
     <permission android:name="android.permission.systemui.IDENTITY"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 2f6e32b..e7e2c1a 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -1,6 +1,6 @@
 set noparent
 
-dsandler@google.com
+dsandler@android.com
 
 adamcohen@google.com
 asc@google.com
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 5317a6d..66d5d11 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -27,7 +27,7 @@
  */
 @ProvidesInterface(version = ActivityStarter.VERSION)
 public interface ActivityStarter {
-    int VERSION = 1;
+    int VERSION = 2;
 
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
 
@@ -37,6 +37,11 @@
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback);
+
+    /**
+     * The intent flag can be specified in startActivity().
+     */
+    void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
     void startActivity(Intent intent, boolean dismissShade);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 7432f9c..41acf82 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -405,7 +405,7 @@
     <!-- Header for typographic clock face. [CHAR LIMIT=8] -->
     <string name="type_clock_header">It\u2019s</string>
 
-    <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=8] -->
+    <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=12] -->
     <string-array name="type_clock_hours">
         <item>Twelve</item>
         <item>One</item>
@@ -421,7 +421,7 @@
         <item>Eleven</item>
     </string-array>
 
-    <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=16] -->
+    <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=20] -->
     <string-array name="type_clock_minutes">
         <item>O\u2019Clock</item>
         <item>O\u2019One</item>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b9966cf..8bbc270 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -103,4 +103,10 @@
         <item name="android:textSize">@dimen/widget_label_font_size</item>
     </style>
 
+    <style name="TextAppearance.Keyguard.BottomArea">
+        <item name="android:textSize">16sp</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_bottompath_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_bottompath_animation.xml
deleted file mode 100644
index 073bf6d..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_bottompath_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="283"
-        android:propertyName="pathData"
-        android:valueFrom="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-        android:valueTo="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-        android:valueType="pathType"
-        android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_circlepath_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_circlepath_animation.xml
deleted file mode 100644
index 990392d..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_circlepath_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="383"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="1.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_ellipse_path_1_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_ellipse_path_1_animation.xml
deleted file mode 100644
index a00d937..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_ellipse_path_1_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="350"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,-1.988645 c 1.09829830627,0.0 1.988645,0.890346693734 1.988645,1.988645 c 0.0,1.09829830627 -0.890346693734,1.988645 -1.988645,1.988645 c -1.09829830627,0.0 -1.988645,-0.890346693734 -1.988645,-1.988645 c 0.0,-1.09829830627 0.890346693734,-1.988645 1.988645,-1.988645 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_6" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_errorcircle_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_errorcircle_animation.xml
deleted file mode 100644
index 59d6232..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_errorcircle_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyName="rotation"
-            android:valueFrom="5.0"
-            android:valueTo="5.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="366"
-            android:propertyName="rotation"
-            android:valueFrom="5.0"
-            android:valueTo="-180.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_errorexclamationdot_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_errorexclamationdot_animation.xml
deleted file mode 100644
index 55bfa58..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_errorexclamationdot_animation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="283"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 12.0,12.0 c 0.0,-0.66667 0.0,-3.33333 0.0,-4.0"
-        android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_5" />
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_exclamationtop_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_exclamationtop_animation.xml
deleted file mode 100644
index 4c44c6b..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_exclamationtop_animation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="283"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0.0,-2.0 c 0.0,0.5 0.0,2.5 0.0,3.0"
-        android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_2" />
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_lock_top_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_lock_top_animation.xml
deleted file mode 100644
index 6f7d692..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_lock_top_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="scaleX"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="66"
-            android:propertyName="scaleX"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="scaleY"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="66"
-            android:propertyName="scaleY"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_path_1_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_path_1_animation.xml
deleted file mode 100644
index 6821e62..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_path_1_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="283"
-            android:propertyName="pathData"
-            android:valueFrom="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.0118713378906,7.9296875 0.0118713378906,7.9296875 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 -0.01123046875,-7.9296875 -0.01123046875,-7.9296875 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueTo="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.0118713378906,7.9296875 0.0118713378906,7.9296875 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 -0.01123046875,-7.9296875 -0.01123046875,-7.9296875 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="pathData"
-            android:valueFrom="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.0118713378906,7.9296875 0.0118713378906,7.9296875 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 -0.01123046875,-7.9296875 -0.01123046875,-7.9296875 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueTo="M 6.00561523438,-4.046875 c 0.0,0.0 -5.98999023438,0.0 -5.98999023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 5.98812866211,0.0 5.98812866211,0.0 c 0.0,0.0 0.00064086914062,10.0 0.00064086914062,10.0 c 0.0,0.0 -5.98840332031,0.0 -5.98840332031,0.0 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 5.99487304688,0.0 5.99487304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 0.0,-10.0 0.0,-10.0 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_3" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_path_2_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_path_2_animation.xml
deleted file mode 100644
index a8251dc..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_path_2_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="283"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.01123046875,7.9296875 0.01123046875,7.9296875 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 -0.0108337402344,-7.92947387695 -0.0108337402344,-7.92947387695 Z"
-            android:valueTo="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.01123046875,7.9296875 0.01123046875,7.9296875 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 -0.0108337402344,-7.92947387695 -0.0108337402344,-7.92947387695 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.01123046875,7.9296875 0.01123046875,7.9296875 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 -0.0108337402344,-7.92947387695 -0.0108337402344,-7.92947387695 Z"
-            android:valueTo="M -5.9959564209,-2.04727172852 c 0.0,0.0 6.01173400879,0.00039672851562 6.01173400879,0.00039672851562 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -6.01000976562,0.0 -6.01000976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.0,10.0 0.0,10.0 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 6.01000976562,0.0 6.01000976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 -6.01171875,0.0003662109375 -6.01171875,0.0003662109375 c 0.0,0.0 0.00038146972656,-9.99978637695 0.00038146972656,-9.99978637695 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_3" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_path_3_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_path_3_animation.xml
deleted file mode 100644
index c1ece08..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_path_3_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="250"
-            android:propertyName="pathData"
-            android:valueFrom="M 5.01239013672,3.390625 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueTo="M 5.01239013672,3.390625 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="pathData"
-            android:valueFrom="M 5.01239013672,3.390625 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueTo="M 5.00619506836,-6.046875 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_4" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/error_to_trustedstate_toppath_animation.xml b/packages/SystemUI/res/anim/error_to_trustedstate_toppath_animation.xml
deleted file mode 100644
index d9781df..0000000
--- a/packages/SystemUI/res/anim/error_to_trustedstate_toppath_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="66"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueTo="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueTo="M 0.0,-6.0 l 0.0,0.0 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 l 0.0,5.0 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 l 0.0,0.0 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 l 0.0,-5.0 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/error_to_trustedstate_animation_interpolator_0" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="283"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_bottompath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_bottompath_animation.xml
deleted file mode 100644
index 91829c0..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_bottompath_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="316"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_5" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_circlepath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_circlepath_animation.xml
deleted file mode 100644
index 9b08fa2..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_circlepath_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="166"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="533"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorcircle_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorcircle_animation.xml
deleted file mode 100644
index 1ea2100..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorcircle_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="rotation"
-            android:valueFrom="190.0"
-            android:valueTo="190.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="549"
-            android:propertyName="rotation"
-            android:valueFrom="190.0"
-            android:valueTo="-6.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_2" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorexclamation_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorexclamation_animation.xml
deleted file mode 100644
index 0b9cb95..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_errorexclamation_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="rotation"
-            android:valueFrom="180.0"
-            android:valueTo="180.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="499"
-            android:propertyName="rotation"
-            android:valueFrom="180.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_6" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationbottom_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationbottom_animation.xml
deleted file mode 100644
index c597b82..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationbottom_animation.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationtop_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationtop_animation.xml
deleted file mode 100644
index bde83de..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_exclamationtop_animation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="700"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0.0,0.0 c 0.0,-0.33333 0.0,-1.66667 0.0,-2.0"
-        android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_3" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprinterror_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprinterror_animation.xml
deleted file mode 100644
index a3afaef..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprinterror_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="566"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="-305.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_2" />
-        <objectAnimator
-            android:duration="1066"
-            android:propertyName="rotation"
-            android:valueFrom="-305.0"
-            android:valueTo="-305.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-        <objectAnimator
-            android:duration="800"
-            android:propertyName="rotation"
-            android:valueFrom="-305.0"
-            android:valueTo="-720.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprintwhite_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprintwhite_animation.xml
deleted file mode 100644
index a3afaef..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_fingerprintwhite_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="566"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="-305.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_2" />
-        <objectAnimator
-            android:duration="1066"
-            android:propertyName="rotation"
-            android:valueFrom="-305.0"
-            android:valueTo="-305.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-        <objectAnimator
-            android:duration="800"
-            android:propertyName="rotation"
-            android:valueFrom="-305.0"
-            android:valueTo="-720.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_0_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_0_animation.xml
deleted file mode 100644
index 1c3a766..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_0_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="133"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="0.0"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_animation.xml
deleted file mode 100644
index 5a9258a..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_1_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="66"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_0_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_0_animation.xml
deleted file mode 100644
index 04b8b80..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_0_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_2" />
-    </set>
-    <objectAnimator
-        android:duration="166"
-        android:propertyName="trimPathStart"
-        android:valueFrom="1.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_2" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_animation.xml
deleted file mode 100644
index c69001c..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_2_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="133"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_0_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_0_animation.xml
deleted file mode 100644
index 6fb22cd..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_0_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="166"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="0.0"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="166"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_animation.xml
deleted file mode 100644
index 8e9a0510..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_5_path_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="150"
-        android:propertyName="trimPathStart"
-        android:valueFrom="0.0"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/linear" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_0_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_0_animation.xml
deleted file mode 100644
index 3ffb8f8..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_0_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="250"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="0.0"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="133"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="199"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_animation.xml
deleted file mode 100644
index 3584f91..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_6_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_0_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_0_animation.xml
deleted file mode 100644
index 0d44810..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="133"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="199"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_animation.xml
deleted file mode 100644
index 52a5b3e..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_ridge_7_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_toerror_toppath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_toerror_toppath_animation.xml
deleted file mode 100644
index ff7420b..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_toerror_toppath_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M -1.0,0.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M -1.0,0.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="499"
-            android:propertyName="pathData"
-            android:valueFrom="M -1.0,0.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/ic_fingerprint_toerror_animation_interpolator_4" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_bottompath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_bottompath_animation.xml
deleted file mode 100644
index 4bc18f2..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_bottompath_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="250"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-            android:valueTo="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-            android:valueTo="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_circlepath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_circlepath_animation.xml
deleted file mode 100644
index 9273b9a..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_circlepath_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="383"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="1.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_4" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorcircle_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorcircle_animation.xml
deleted file mode 100644
index 062c6a0..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorcircle_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyName="rotation"
-            android:valueFrom="10.0"
-            android:valueTo="10.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="366"
-            android:propertyName="rotation"
-            android:valueFrom="10.0"
-            android:valueTo="-180.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_2" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorexclamation_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorexclamation_animation.xml
deleted file mode 100644
index 5d4b268..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_errorexclamation_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="416"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="-180.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_5" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationbottom_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationbottom_animation.xml
deleted file mode 100644
index d3d7d1c..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationbottom_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="450"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0.0,4.0 c 0.0,-0.66667 0.0,-3.33333 0.0,-4.0"
-        android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationtop_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationtop_animation.xml
deleted file mode 100644
index 04b6868..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_exclamationtop_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="450"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0.0,-2.0 c 0.0,0.33333 0.0,1.66667 0.0,2.0"
-        android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_3" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_fingerprintwhite_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_fingerprintwhite_animation.xml
deleted file mode 100644
index 25f2c43..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_fingerprintwhite_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="rotation"
-            android:valueFrom="180.0"
-            android:valueTo="180.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="499"
-            android:propertyName="rotation"
-            android:valueFrom="180.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_2" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_1_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_1_path_animation.xml
deleted file mode 100644
index 4a6f39a..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_1_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="416"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_2_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_2_path_animation.xml
deleted file mode 100644
index c9a9f64..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_2_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="350"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_5_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_5_path_animation.xml
deleted file mode 100644
index 43e44cf..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_5_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="350"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="350"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_6_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_6_path_animation.xml
deleted file mode 100644
index 04a6036..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_6_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="250"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="250"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_7_path_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_7_path_animation.xml
deleted file mode 100644
index 031c5e6..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_ridge_7_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="266"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="433"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_fingerprint_tofp_toppath_animation.xml b/packages/SystemUI/res/anim/ic_fingerprint_tofp_toppath_animation.xml
deleted file mode 100644
index 9b3498f..0000000
--- a/packages/SystemUI/res/anim/ic_fingerprint_tofp_toppath_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueTo="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="250"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueTo="M -1.0,0.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/ic_fingerprint_tofp_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_1_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_1_path_animation.xml
deleted file mode 100644
index 18bc8a2..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_1_path_animation.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="60"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_off_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_2_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_2_path_animation.xml
deleted file mode 100644
index 531fac0..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_2_path_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="140"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="1.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/lockscreen_fingerprint_draw_off_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_5_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_5_path_animation.xml
deleted file mode 100644
index 87c7f21..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_5_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="20"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="180"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_off_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_6_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_6_path_animation.xml
deleted file mode 100644
index e0a66d9..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_6_path_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="190"
-        android:propertyName="trimPathEnd"
-        android:valueFrom="1.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/lockscreen_fingerprint_draw_off_animation_interpolator_1" />
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_7_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_7_path_animation.xml
deleted file mode 100644
index 9b0e17b..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_off_ridge_7_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="10"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="160"
-            android:propertyName="trimPathStart"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_off_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_1_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_1_path_animation.xml
deleted file mode 100644
index eb323f5..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_1_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="316"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="383"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_on_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_2_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_2_path_animation.xml
deleted file mode 100644
index cae4a70..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_2_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="400"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_on_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_5_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_5_path_animation.xml
deleted file mode 100644
index 0be6c9e..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_5_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="383"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_on_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_6_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_6_path_animation.xml
deleted file mode 100644
index 5d2cdb6..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_6_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="549"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_on_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_7_path_animation.xml b/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_7_path_animation.xml
deleted file mode 100644
index 6f8b559..0000000
--- a/packages/SystemUI/res/anim/lockscreen_fingerprint_draw_on_ridge_7_path_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="483"
-            android:propertyName="trimPathEnd"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/lockscreen_fingerprint_draw_on_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_bottompath_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_bottompath_animation.xml
deleted file mode 100644
index 7a01896..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_bottompath_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="149"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-            android:valueTo="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_circlepath_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_circlepath_animation.xml
deleted file mode 100644
index ac5e448..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_circlepath_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="450"
-            android:propertyName="trimPathStart"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_ellipse_path_1_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_ellipse_path_1_animation.xml
deleted file mode 100644
index 6697316..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_ellipse_path_1_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="200"
-        android:propertyName="pathData"
-        android:valueFrom="M 0.0,-1.988645 c 1.09829830627,0.0 1.988645,0.890346693734 1.988645,1.988645 c 0.0,1.09829830627 -0.890346693734,1.988645 -1.988645,1.988645 c -1.09829830627,0.0 -1.988645,-0.890346693734 -1.988645,-1.988645 c 0.0,-1.09829830627 0.890346693734,-1.988645 1.988645,-1.988645 Z"
-        android:valueTo="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
-        android:valueType="pathType"
-        android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_errorcircle_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_errorcircle_animation.xml
deleted file mode 100644
index 903d97e..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_errorcircle_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="rotation"
-            android:valueFrom="184.0"
-            android:valueTo="184.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="450"
-            android:propertyName="rotation"
-            android:valueFrom="184.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_1" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_errorexclamationdot_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_errorexclamationdot_animation.xml
deleted file mode 100644
index c5339a0..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_errorexclamationdot_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="550"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 12.0,8.0 c 0.0,0.66667 0.0,3.33333 0.0,4.0"
-        android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_3" />
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_exclamationtop_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_exclamationtop_animation.xml
deleted file mode 100644
index c5b2c12..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_exclamationtop_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="500"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0.0,2.5 c 0.0,-0.75 0.0,-3.75 0.0,-4.5"
-        android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_4" />
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_lock_top_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_lock_top_animation.xml
deleted file mode 100644
index 63a25d9..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_lock_top_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_path_1_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_path_1_animation.xml
deleted file mode 100644
index 547f42e..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_path_1_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M 6.00561523438,-4.046875 c 0.0,0.0 -5.98999023438,0.0 -5.98999023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 5.98812866211,0.0 5.98812866211,0.0 c 0.0,0.0 0.00064086914062,10.0 0.00064086914062,10.0 c 0.0,0.0 -5.98840332031,0.0 -5.98840332031,0.0 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 5.99487304688,0.0 5.99487304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 0.0,-10.0 0.0,-10.0 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueTo="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.00064086914062,10.625 0.00064086914062,10.625 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 0.0,-10.625 0.0,-10.625 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_0" />
-        <objectAnimator
-            android:duration="149"
-            android:propertyName="pathData"
-            android:valueFrom="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.00064086914062,10.625 0.00064086914062,10.625 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 0.0,-10.625 0.0,-10.625 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-            android:valueTo="M 0.02685546875,-4.96875 c 0.0,0.0 -0.005615234375,0.015625 -0.005615234375,0.015625 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 -0.00936889648438,3.9296875 -0.00936889648438,3.9296875 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0028076171875,0.0234375 -0.0028076171875,0.0234375 c 0.0,0.0 0.010498046875,-0.015625 0.010498046875,-0.015625 c 0.0,0.0 0.985595703125,0.0078125 0.985595703125,0.0078125 c 0.0,0.0 0.017578125,-6.015625 0.017578125,-6.015625 c 0.0,0.0 0.104400634766,0.0546875 -0.99560546875,0.0546875 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_5" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="183"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_path_2_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_path_2_animation.xml
deleted file mode 100644
index e5fe4d1..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_path_2_animation.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M -5.9959564209,-2.04727172852 c 0.0,0.0 6.01173400879,0.00039672851562 6.01173400879,0.00039672851562 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -6.01000976562,0.0 -6.01000976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.0,10.0 0.0,10.0 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 6.01000976562,0.0 6.01000976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 -6.01171875,0.0003662109375 -6.01171875,0.0003662109375 c 0.0,0.0 0.00038146972656,-9.99978637695 0.00038146972656,-9.99978637695 Z"
-            android:valueTo="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.0,10.625 0.0,10.625 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 0.00039672851562,-10.624786377 0.00039672851562,-10.624786377 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_0" />
-        <objectAnimator
-            android:duration="149"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.0,10.625 0.0,10.625 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 0.00039672851562,-10.624786377 0.00039672851562,-10.624786377 Z"
-            android:valueTo="M 0.0252990722656,-2.96914672852 c 0.0,0.0 -0.00390625,0.0160217285156 -0.00390625,0.0160217285156 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 0.005615234375,-0.015625 0.005615234375,-0.015625 c -1.10000610352,0.0 -1.01220703125,-0.0546875 -1.01220703125,-0.0546875 c 0.0,0.0 -0.017578125,6.015625 -0.017578125,6.015625 c 0.0,0.0 -0.0777893066406,-0.0078125 1.02221679688,-0.0078125 c 0.0,0.0 -0.005615234375,0.015625 -0.005615234375,0.015625 c 0.0,0.0 -0.002685546875,-0.0244140625 -0.002685546875,-0.0244140625 c 0.0,0.0 0.00390625,-0.0152587890625 0.00390625,-0.0152587890625 c 0.0,0.0 0.0104064941406,-3.92947387695 0.0104064941406,-3.92947387695 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_5" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="183"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.5"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.5"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_path_3_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_path_3_animation.xml
deleted file mode 100644
index 455d0b8..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_path_3_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="pathData"
-            android:valueFrom="M 5.00619506836,-6.046875 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueTo="M 5.00619506836,-6.046875 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="pathData"
-            android:valueFrom="M 5.00619506836,-6.046875 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueTo="M 5.01239013672,3.390625 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_0" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/trusted_state_to_error_toppath_animation.xml b/packages/SystemUI/res/anim/trusted_state_to_error_toppath_animation.xml
deleted file mode 100644
index b2944e5..0000000
--- a/packages/SystemUI/res/anim/trusted_state_to_error_toppath_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-7.0 l 0.0,0.0 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 l 0.0,7.0 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 l 0.0,0.0 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 l 0.0,-7.0 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
-            android:valueTo="M 0.0,-7.0 l 0.0,0.0 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 l 0.0,7.0 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 l 0.0,0.0 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 l 0.0,-7.0 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="300"
-            android:propertyName="pathData"
-            android:valueFrom="M 0.0,-7.0 l 0.0,0.0 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 l 0.0,7.0 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 l 0.0,0.0 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 l 0.0,-7.0 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
-            android:valueTo="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z"
-            android:valueType="pathType"
-            android:interpolator="@interpolator/trusted_state_to_error_animation_interpolator_2" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="183"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="16"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/drawable/error_to_trustedstate.xml b/packages/SystemUI/res/drawable/error_to_trustedstate.xml
deleted file mode 100755
index bc196c9..0000000
--- a/packages/SystemUI/res/drawable/error_to_trustedstate.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="error_to_trustedstate"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:height="24dp"
-    android:viewportHeight="24" >
-    <group
-        android:name="lock"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="middle_ellipse"
-            android:translateY="2.9375" >
-            <path
-                android:name="ellipse_path_1"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5"
-                android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-        </group>
-        <group
-            android:name="lock_right_side" >
-            <path
-                android:name="path_1"
-                android:pathData="M 1.63623046875,-4.953125 c 0.0,0.0 -1.61499023438,0.0 -1.61499023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 0.00375366210938,-0.015625 0.00375366210938,-0.015625 c 0.0,0.0 0.0118713378906,7.9296875 0.0118713378906,7.9296875 c 0.0,0.0 -0.0040283203125,0.015625 -0.0040283203125,0.015625 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 1.61987304688,0.0 1.61987304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 -0.01123046875,-7.9296875 -0.01123046875,-7.9296875 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-        <group
-            android:name="lock_left_side" >
-            <path
-                android:name="path_2"
-                android:pathData="M 0.0252990722656,-2.96975708008 c 0.0,0.0 -0.00390625,0.0166320800781 -0.00390625,0.0166320800781 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -1.63500976562,0.0 -1.63500976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.01123046875,7.9296875 0.01123046875,7.9296875 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 1.63500976562,0.0 1.63500976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 0.00390625,-0.015869140625 0.00390625,-0.015869140625 c 0.0,0.0 -0.0108337402344,-7.92947387695 -0.0108337402344,-7.92947387695 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-        <group
-            android:name="lock_top"
-            android:scaleX="0"
-            android:scaleY="0" >
-            <path
-                android:name="path_3"
-                android:pathData="M 5.01239013672,3.390625 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamationdot"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="exclamationbottom"
-            android:translateY="4" >
-            <path
-                android:name="bottompath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamation"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="exclamationtop"
-            android:translateY="-2" >
-            <path
-                android:name="toppath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorcircle"
-        android:translateX="12"
-        android:translateY="12"
-        android:rotation="5" >
-        <group
-            android:name="circle" >
-            <path
-                android:name="circlepath"
-                android:strokeColor="?android:attr/colorError"
-                android:strokeWidth="2"
-                android:strokeLineCap="round"
-                android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/error_to_trustedstate_animation.xml b/packages/SystemUI/res/drawable/error_to_trustedstate_animation.xml
deleted file mode 100755
index a494f1d..0000000
--- a/packages/SystemUI/res/drawable/error_to_trustedstate_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/error_to_trustedstate" >
-    <target
-        android:name="ellipse_path_1"
-        android:animation="@anim/error_to_trustedstate_ellipse_path_1_animation" />
-    <target
-        android:name="path_1"
-        android:animation="@anim/error_to_trustedstate_path_1_animation" />
-    <target
-        android:name="path_2"
-        android:animation="@anim/error_to_trustedstate_path_2_animation" />
-    <target
-        android:name="lock_top"
-        android:animation="@anim/error_to_trustedstate_lock_top_animation" />
-    <target
-        android:name="path_3"
-        android:animation="@anim/error_to_trustedstate_path_3_animation" />
-    <target
-        android:name="errorexclamationdot"
-        android:animation="@anim/error_to_trustedstate_errorexclamationdot_animation" />
-    <target
-        android:name="bottompath"
-        android:animation="@anim/error_to_trustedstate_bottompath_animation" />
-    <target
-        android:name="exclamationtop"
-        android:animation="@anim/error_to_trustedstate_exclamationtop_animation" />
-    <target
-        android:name="toppath"
-        android:animation="@anim/error_to_trustedstate_toppath_animation" />
-    <target
-        android:name="errorcircle"
-        android:animation="@anim/error_to_trustedstate_errorcircle_animation" />
-    <target
-        android:name="circlepath"
-        android:animation="@anim/error_to_trustedstate_circlepath_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_face_unlock.xml b/packages/SystemUI/res/drawable/ic_face_unlock.xml
deleted file mode 100644
index b302ed4..0000000
--- a/packages/SystemUI/res/drawable/ic_face_unlock.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportHeight="24.0"
-        android:viewportWidth="24.0">
-    <path android:fillColor="?attr/wallpaperTextColor"
-          android:strokeColor="?attr/wallpaperTextColor"
-          android:strokeWidth="1"
-          android:pathData="M9,11.75C8.31,11.75 7.75,12.31 7.75,13C7.75,13.69 8.31,14.25 9,14.25C9.69,14.25 10.25,13.69 10.25,13C10.25,12.31 9.69,11.75 9,11.75ZM15,11.75C14.31,11.75 13.75,12.31 13.75,13C13.75,13.69 14.31,14.25 15,14.25C15.69,14.25 16.25,13.69 16.25,13C16.25,12.31 15.69,11.75 15,11.75ZM12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,11.71 4.02,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z"
-    />
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
deleted file mode 100644
index a7fb1a1..0000000
--- a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0">
-    <path
-        android:fillColor="?android:attr/colorError"
-        android:pathData="M15.99,2.5C8.53,2.5 2.5,8.54 2.5,16.0s6.03,13.5 13.49,13.5S29.5,23.46 29.5,16.0S23.45,2.5 15.99,2.5zM16.0,26.8c-5.97,0.0 -10.8,-4.83 -10.8,-10.8S10.03,5.2 16.0,5.2S26.8,10.03 26.8,16.0S21.97,26.8 16.0,26.8z"/>
-    <path
-        android:fillColor="?android:attr/colorError"
-        android:pathData="M14.65,20.05l2.7,0.0l0.0,2.7l-2.7,0.0z"/>
-    <path
-        android:fillColor="?android:attr/colorError"
-        android:pathData="M14.65,9.25l2.7,0.0l0.0,8.1l-2.7,0.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock.xml b/packages/SystemUI/res/drawable/ic_lock.xml
deleted file mode 100644
index 3fb8c8d..0000000
--- a/packages/SystemUI/res/drawable/ic_lock.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0">
-    <path
-        android:pathData="M20.25,6.89C20.25,4.62 18.36,2.75 16,2.75C13.64,2.75 11.75,4.62 11.75,6.89V11.25H20.25V6.89ZM21.75,11.25V6.89C21.75,3.76 19.16,1.25 16,1.25C12.84,1.25 10.25,3.76 10.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
-        android:fillType="evenOdd"
-        android:fillColor="?attr/wallpaperTextColor"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open.xml b/packages/SystemUI/res/drawable/ic_lock_open.xml
deleted file mode 100644
index 12a811c..0000000
--- a/packages/SystemUI/res/drawable/ic_lock_open.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0">
-    <path
-        android:pathData="M21.75,6.89C21.75,4.62 23.64,2.75 26,2.75C28.36,2.75 30.25,4.62 30.25,6.89V12H31.75V6.89C31.75,3.76 29.16,1.25 26,1.25C22.84,1.25 20.25,3.76 20.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75V6.89ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
-        android:fillType="evenOdd"
-        android:fillColor="?attr/wallpaperTextColor"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new.xml b/packages/SystemUI/res/drawable/ic_open_in_new.xml
new file mode 100644
index 0000000..3d53871
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_open_in_new.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2019 The Android Open Source Project
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at"+
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off.xml
deleted file mode 100644
index 8d382a3..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="lockscreen_fingerprint_draw_off"
-    android:width="32dp"
-    android:viewportWidth="32"
-    android:height="32dp"
-    android:viewportHeight="32" >
-    <group
-        android:name="white_fingerprint_ridges"
-        android:translateX="16.125"
-        android:translateY="19.75" >
-        <group
-            android:name="white_fingerprint_ridges_pivot"
-            android:translateX="33.2085"
-            android:translateY="30.91685" >
-            <group
-                android:name="ridge_5" >
-                <path
-                    android:name="ridge_5_path"
-                    android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_4" >
-                <path
-                    android:name="ridge_7_path"
-                    android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_3" >
-                <path
-                    android:name="ridge_6_path"
-                    android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_2" >
-                <path
-                    android:name="ridge_2_path"
-                    android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_1"
-                android:translateX="-97.5"
-                android:translateY="-142.5" >
-                <path
-                    android:name="ridge_1_path"
-                    android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off_animation.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off_animation.xml
deleted file mode 100644
index 1599577..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_off_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/lockscreen_fingerprint_draw_off" >
-    <target
-        android:name="ridge_5_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_off_ridge_5_path_animation" />
-    <target
-        android:name="ridge_7_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_off_ridge_7_path_animation" />
-    <target
-        android:name="ridge_6_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_off_ridge_6_path_animation" />
-    <target
-        android:name="ridge_2_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_off_ridge_2_path_animation" />
-    <target
-        android:name="ridge_1_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_off_ridge_1_path_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on.xml
deleted file mode 100644
index 4fe160d..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="lockscreen_fingerprint_draw_on"
-    android:width="32dp"
-    android:viewportWidth="32"
-    android:height="32dp"
-    android:viewportHeight="32" >
-    <group
-        android:name="white_fingerprint_ridges"
-        android:translateX="16.125"
-        android:translateY="19.75" >
-        <group
-            android:name="white_fingerprint_ridges_pivot"
-            android:translateX="33.2085"
-            android:translateY="30.91685" >
-            <group
-                android:name="ridge_5" >
-                <path
-                    android:name="ridge_5_path"
-                    android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_4" >
-                <path
-                    android:name="ridge_7_path"
-                    android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_3" >
-                <path
-                    android:name="ridge_6_path"
-                    android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathStart="1" />
-            </group>
-            <group
-                android:name="ridge_2" >
-                <path
-                    android:name="ridge_2_path"
-                    android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathStart="1" />
-            </group>
-            <group
-                android:name="ridge_1"
-                android:translateX="-97.5"
-                android:translateY="-142.5" >
-                <path
-                    android:name="ridge_1_path"
-                    android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on_animation.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on_animation.xml
deleted file mode 100644
index d1aac02..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_draw_on_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/lockscreen_fingerprint_draw_on" >
-    <target
-        android:name="ridge_5_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_on_ridge_5_path_animation" />
-    <target
-        android:name="ridge_7_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_on_ridge_7_path_animation" />
-    <target
-        android:name="ridge_6_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_on_ridge_6_path_animation" />
-    <target
-        android:name="ridge_2_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_on_ridge_2_path_animation" />
-    <target
-        android:name="ridge_1_path"
-        android:animation="@anim/lockscreen_fingerprint_draw_on_ridge_1_path_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
deleted file mode 100644
index 832716a..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2017 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_fingerprint_tofp"
-    android:width="32dp"
-    android:viewportWidth="24"
-    android:height="32dp"
-    android:viewportHeight="24" >
-    <group
-        android:name="fingerprintwhite"
-        android:translateX="12"
-        android:translateY="12.4"
-        android:scaleX="0.738"
-        android:scaleY="0.738"
-        android:rotation="180" >
-        <group
-            android:name="fingerprintwhite_pivot"
-            android:translateX="33"
-            android:translateY="34" >
-            <group
-                android:name="ridge_5" >
-                <path
-                    android:name="ridge_5_path"
-                    android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_4" >
-                <path
-                    android:name="ridge_7_path"
-                    android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_3" >
-                <path
-                    android:name="ridge_6_path"
-                    android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathStart="1" />
-            </group>
-            <group
-                android:name="ridge_2" >
-                <path
-                    android:name="ridge_2_path"
-                    android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathStart="1" />
-            </group>
-            <group
-                android:name="ridge_1"
-                android:translateX="-97.5"
-                android:translateY="-142.5" >
-                <path
-                    android:name="ridge_1_path"
-                    android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-        </group>
-    </group>
-    <group
-        android:name="errorcircle"
-        android:translateX="12"
-        android:translateY="12"
-        android:rotation="10" >
-        <group
-            android:name="circle" >
-            <path
-                android:name="circlepath"
-                android:strokeColor="?android:attr/colorError"
-                android:strokeWidth="2"
-                android:strokeLineCap="round"
-                android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamation"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="exclamationbottom"
-            android:translateY="4" >
-            <path
-                android:name="bottompath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,-1.1 l 0.0,0.0 c 0.60751322478,0.0 1.1,0.49248677522 1.1,1.1 l 0.0,0.0 c 0.0,0.60751322478 -0.49248677522,1.1 -1.1,1.1 l 0.0,0.0 c -0.60751322478,0.0 -1.1,-0.49248677522 -1.1,-1.1 l 0.0,0.0 c 0.0,-0.60751322478 0.49248677522,-1.1 1.1,-1.1 Z" />
-        </group>
-        <group
-            android:name="exclamationtop"
-            android:translateY="-2" >
-            <path
-                android:name="toppath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,-3.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,4.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,-4.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp_animation.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp_animation.xml
deleted file mode 100644
index 2f33306..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp_animation.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/lockscreen_fingerprint_error_state_to_fp" >
-    <target
-        android:name="fingerprintwhite"
-        android:animation="@anim/ic_fingerprint_tofp_fingerprintwhite_animation" />
-    <target
-        android:name="ridge_5_path"
-        android:animation="@anim/ic_fingerprint_tofp_ridge_5_path_animation" />
-    <target
-        android:name="ridge_7_path"
-        android:animation="@anim/ic_fingerprint_tofp_ridge_7_path_animation" />
-    <target
-        android:name="ridge_6_path"
-        android:animation="@anim/ic_fingerprint_tofp_ridge_6_path_animation" />
-    <target
-        android:name="ridge_2_path"
-        android:animation="@anim/ic_fingerprint_tofp_ridge_2_path_animation" />
-    <target
-        android:name="ridge_1_path"
-        android:animation="@anim/ic_fingerprint_tofp_ridge_1_path_animation" />
-    <target
-        android:name="errorcircle"
-        android:animation="@anim/ic_fingerprint_tofp_errorcircle_animation" />
-    <target
-        android:name="circlepath"
-        android:animation="@anim/ic_fingerprint_tofp_circlepath_animation" />
-    <target
-        android:name="errorexclamation"
-        android:animation="@anim/ic_fingerprint_tofp_errorexclamation_animation" />
-    <target
-        android:name="exclamationbottom"
-        android:animation="@anim/ic_fingerprint_tofp_exclamationbottom_animation" />
-    <target
-        android:name="bottompath"
-        android:animation="@anim/ic_fingerprint_tofp_bottompath_animation" />
-    <target
-        android:name="exclamationtop"
-        android:animation="@anim/ic_fingerprint_tofp_exclamationtop_animation" />
-    <target
-        android:name="toppath"
-        android:animation="@anim/ic_fingerprint_tofp_toppath_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
deleted file mode 100644
index 7cc2e5c..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="ic_fingerprint_toerror"
-    android:width="32dp"
-    android:viewportWidth="24"
-    android:height="32dp"
-    android:viewportHeight="24" >
-    <group
-        android:name="fingerprintwhite"
-        android:translateX="12"
-        android:translateY="12.4"
-        android:scaleX="0.738"
-        android:scaleY="0.738" >
-        <group
-            android:name="fingerprintwhite_pivot"
-            android:translateX="33"
-            android:translateY="34" >
-            <group
-                android:name="ridge_5" >
-                <path
-                    android:name="ridge_5_path"
-                    android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_4" >
-                <path
-                    android:name="ridge_7_path"
-                    android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_3" >
-                <path
-                    android:name="ridge_6_path"
-                    android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_2" >
-                <path
-                    android:name="ridge_2_path"
-                    android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-            <group
-                android:name="ridge_1"
-                android:translateX="-97.5"
-                android:translateY="-142.5" >
-                <path
-                    android:name="ridge_1_path"
-                    android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
-                    android:strokeColor="?attr/wallpaperTextColor"
-                    android:strokeAlpha="0.5"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round" />
-            </group>
-        </group>
-    </group>
-    <group
-        android:name="fingerprinterror"
-        android:translateX="12"
-        android:translateY="12.4"
-        android:scaleX="0.738"
-        android:scaleY="0.738" >
-        <group
-            android:name="fingerprinterror_pivot"
-            android:translateX="33"
-            android:translateY="34" >
-            <group
-                android:name="ridge_6" >
-                <path
-                    android:name="ridge_5_path_0"
-                    android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
-                    android:strokeColor="?android:attr/colorError"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_7" >
-                <path
-                    android:name="ridge_7_path_0"
-                    android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
-                    android:strokeColor="?android:attr/colorError"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_8" >
-                <path
-                    android:name="ridge_6_path_0"
-                    android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
-                    android:strokeColor="?android:attr/colorError"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-            <group
-                android:name="ridge_9" >
-                <path
-                    android:name="ridge_2_path_0"
-                    android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
-                    android:strokeColor="?android:attr/colorError"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathStart="1" />
-            </group>
-            <group
-                android:name="ridge_10"
-                android:translateX="-97.5"
-                android:translateY="-142.5" >
-                <path
-                    android:name="ridge_1_path_0"
-                    android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
-                    android:strokeColor="?android:attr/colorError"
-                    android:strokeWidth="1.45"
-                    android:strokeLineCap="round"
-                    android:trimPathEnd="0" />
-            </group>
-        </group>
-    </group>
-    <group
-        android:name="errorcircle"
-        android:translateX="12"
-        android:translateY="12"
-        android:rotation="190" >
-        <group
-            android:name="circle" >
-            <path
-                android:name="circlepath"
-                android:strokeColor="?android:attr/colorError"
-                android:strokeWidth="2"
-                android:strokeLineCap="round"
-                android:trimPathStart="1"
-                android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamation"
-        android:translateX="12"
-        android:translateY="12"
-        android:rotation="180" >
-        <group
-            android:name="exclamationbottom"
-            android:translateY="4" >
-            <path
-                android:name="bottompath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-        </group>
-        <group
-            android:name="exclamationtop" >
-            <path
-                android:name="toppath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M -1.0,0.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state_animation.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state_animation.xml
deleted file mode 100644
index 38096a9..0000000
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state_animation.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/lockscreen_fingerprint_fp_to_error_state" >
-    <target
-        android:name="fingerprintwhite"
-        android:animation="@anim/ic_fingerprint_toerror_fingerprintwhite_animation" />
-    <target
-        android:name="ridge_5_path"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_5_path_animation" />
-    <target
-        android:name="ridge_7_path"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_7_path_animation" />
-    <target
-        android:name="ridge_6_path"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_6_path_animation" />
-    <target
-        android:name="ridge_2_path"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_2_path_animation" />
-    <target
-        android:name="ridge_1_path"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_1_path_animation" />
-    <target
-        android:name="fingerprinterror"
-        android:animation="@anim/ic_fingerprint_toerror_fingerprinterror_animation" />
-    <target
-        android:name="ridge_5_path_0"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_5_path_0_animation" />
-    <target
-        android:name="ridge_7_path_0"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_7_path_0_animation" />
-    <target
-        android:name="ridge_6_path_0"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_6_path_0_animation" />
-    <target
-        android:name="ridge_2_path_0"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_2_path_0_animation" />
-    <target
-        android:name="ridge_1_path_0"
-        android:animation="@anim/ic_fingerprint_toerror_ridge_1_path_0_animation" />
-    <target
-        android:name="errorcircle"
-        android:animation="@anim/ic_fingerprint_toerror_errorcircle_animation" />
-    <target
-        android:name="circlepath"
-        android:animation="@anim/ic_fingerprint_toerror_circlepath_animation" />
-    <target
-        android:name="errorexclamation"
-        android:animation="@anim/ic_fingerprint_toerror_errorexclamation_animation" />
-    <target
-        android:name="exclamationbottom"
-        android:animation="@anim/ic_fingerprint_toerror_exclamationbottom_animation" />
-    <target
-        android:name="bottompath"
-        android:animation="@anim/ic_fingerprint_toerror_bottompath_animation" />
-    <target
-        android:name="exclamationtop"
-        android:animation="@anim/ic_fingerprint_toerror_exclamationtop_animation" />
-    <target
-        android:name="toppath"
-        android:animation="@anim/ic_fingerprint_toerror_toppath_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/trusted_state_to_error.xml b/packages/SystemUI/res/drawable/trusted_state_to_error.xml
deleted file mode 100755
index 4a8e452..0000000
--- a/packages/SystemUI/res/drawable/trusted_state_to_error.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:name="trusted_state_to_error"
-    android:width="24dp"
-    android:viewportWidth="24"
-    android:height="24dp"
-    android:viewportHeight="24" >
-    <group
-        android:name="lock"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="middle_ellipse"
-            android:translateY="2.9375" >
-            <path
-                android:name="ellipse_path_1"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5"
-                android:pathData="M 0.0,-1.988645 c 1.09829830627,0.0 1.988645,0.890346693734 1.988645,1.988645 c 0.0,1.09829830627 -0.890346693734,1.988645 -1.988645,1.988645 c -1.09829830627,0.0 -1.988645,-0.890346693734 -1.988645,-1.988645 c 0.0,-1.09829830627 0.890346693734,-1.988645 1.988645,-1.988645 Z" />
-        </group>
-        <group
-            android:name="lock_right_side" >
-            <path
-                android:name="path_1"
-                android:pathData="M 6.00561523438,-4.046875 c 0.0,0.0 -5.98999023438,0.0 -5.98999023438,0.0 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 5.98812866211,0.0 5.98812866211,0.0 c 0.0,0.0 0.00064086914062,10.0 0.00064086914062,10.0 c 0.0,0.0 -5.98840332031,0.0 -5.98840332031,0.0 c 0.0,0.0 -0.0052490234375,2.0 -0.0052490234375,2.0 c 0.0,0.0 5.99487304688,0.0 5.99487304688,0.0 c 1.10000610352,0.0 2.0,-0.900024414062 2.0,-2.0 c 0.0,0.0 0.0,-10.0 0.0,-10.0 c 0.0,-1.09997558594 -0.899993896484,-2.0 -2.0,-2.0 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-        <group
-            android:name="lock_left_side" >
-            <path
-                android:name="path_2"
-                android:pathData="M -5.9959564209,-2.04727172852 c 0.0,0.0 6.01173400879,0.00039672851562 6.01173400879,0.00039672851562 c 0.0,0.0 -0.00015258789062,-2.0 -0.00015258789062,-2.0 c 0.0,0.0 -6.01000976562,0.0 -6.01000976562,0.0 c -1.10000610352,0.0 -2.0,0.900024414062 -2.0,2.0 c 0.0,0.0 0.0,10.0 0.0,10.0 c 0.0,1.09997558594 0.899993896484,2.0 2.0,2.0 c 0.0,0.0 6.01000976562,0.0 6.01000976562,0.0 c 0.0,0.0 -0.000244140625,-2.0009765625 -0.000244140625,-2.0009765625 c 0.0,0.0 -6.01171875,0.0003662109375 -6.01171875,0.0003662109375 c 0.0,0.0 0.00038146972656,-9.99978637695 0.00038146972656,-9.99978637695 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-        <group
-            android:name="lock_top" >
-            <path
-                android:name="path_3"
-                android:pathData="M 5.00619506836,-6.046875 c 0.0,-2.76000976562 -2.23999023438,-5.0 -5.0,-5.0 c -2.76000976562,0.0 -5.0,2.23999023438 -5.0,5.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,-1.71002197266 1.38999938965,-3.09997558594 3.10000610352,-3.09997558594 c 1.71000671387,0.0 3.10000610352,1.38995361328 3.10000610352,3.09997558594 c 0.0,0.0 0.0,2.0 0.0,2.0 c 0.0,0.0 1.89999389648,0.0 1.89999389648,0.0 c 0.0,0.0 0.0,-2.0 0.0,-2.0 Z"
-                android:fillColor="?attr/wallpaperTextColor"
-                android:fillAlpha="0.5" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamationdot"
-        android:translateX="12"
-        android:translateY="8" >
-        <group
-            android:name="exclamationbottom"
-            android:translateY="4" >
-            <path
-                android:name="bottompath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorexclamation"
-        android:translateX="12"
-        android:translateY="12" >
-        <group
-            android:name="exclamationtop"
-            android:translateY="2.5" >
-            <path
-                android:name="toppath"
-                android:fillColor="?android:attr/colorError"
-                android:pathData="M 0.0,-7.0 l 0.0,0.0 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 l 0.0,7.0 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 l 0.0,0.0 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 l 0.0,-7.0 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z" />
-        </group>
-    </group>
-    <group
-        android:name="errorcircle"
-        android:translateX="12"
-        android:translateY="12"
-        android:rotation="184" >
-        <group
-            android:name="circle" >
-            <path
-                android:name="circlepath"
-                android:strokeColor="?android:attr/colorError"
-                android:strokeWidth="2"
-                android:strokeLineCap="round"
-                android:trimPathStart="1"
-                android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/trusted_state_to_error_animation.xml b/packages/SystemUI/res/drawable/trusted_state_to_error_animation.xml
deleted file mode 100755
index 3457be5..0000000
--- a/packages/SystemUI/res/drawable/trusted_state_to_error_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animated-vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/trusted_state_to_error" >
-    <target
-        android:name="ellipse_path_1"
-        android:animation="@anim/trusted_state_to_error_ellipse_path_1_animation" />
-    <target
-        android:name="path_1"
-        android:animation="@anim/trusted_state_to_error_path_1_animation" />
-    <target
-        android:name="path_2"
-        android:animation="@anim/trusted_state_to_error_path_2_animation" />
-    <target
-        android:name="lock_top"
-        android:animation="@anim/trusted_state_to_error_lock_top_animation" />
-    <target
-        android:name="path_3"
-        android:animation="@anim/trusted_state_to_error_path_3_animation" />
-    <target
-        android:name="errorexclamationdot"
-        android:animation="@anim/trusted_state_to_error_errorexclamationdot_animation" />
-    <target
-        android:name="bottompath"
-        android:animation="@anim/trusted_state_to_error_bottompath_animation" />
-    <target
-        android:name="exclamationtop"
-        android:animation="@anim/trusted_state_to_error_exclamationtop_animation" />
-    <target
-        android:name="toppath"
-        android:animation="@anim/trusted_state_to_error_toppath_animation" />
-    <target
-        android:name="errorcircle"
-        android:animation="@anim/trusted_state_to_error_errorcircle_animation" />
-    <target
-        android:name="circlepath"
-        android:animation="@anim/trusted_state_to_error_circlepath_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 1aeb52c..f0d2b2e 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
+    android:orientation="vertical"
     android:id="@+id/bubble_expanded_view">
 
     <View
@@ -26,17 +27,48 @@
         android:layout_height="@dimen/bubble_pointer_height"
     />
 
-    <TextView
-        android:id="@+id/bubble_content_header"
-        android:background="@drawable/bubble_expanded_header_bg"
-        android:textAppearance="@*android:style/TextAppearance.Material.Title"
-        android:textSize="18sp"
-        android:layout_width="match_parent"
+    <LinearLayout
+        android:id="@+id/header_layout"
         android:layout_height="@dimen/bubble_expanded_header_height"
-        android:gravity="start|center_vertical"
-        android:singleLine="true"
-        android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
-        android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
-    />
+        android:layout_width="match_parent"
+        android:orientation="horizontal"
+        android:background="@drawable/bubble_expanded_header_bg">
+
+        <TextView
+            android:id="@+id/header_text"
+            android:textAppearance="@*android:style/TextAppearance.Material.Title"
+            android:textSize="18sp"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:gravity="start|center_vertical"
+            android:singleLine="true"
+            android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
+            android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
+        />
+
+        <ImageButton
+            android:id="@+id/deep_link_button"
+            android:layout_width="@dimen/bubble_header_icon_size"
+            android:layout_height="@dimen/bubble_header_icon_size"
+            android:gravity="end|center_vertical"
+            android:src="@drawable/ic_open_in_new"
+            android:scaleType="center"
+            android:tint="?android:attr/colorForeground"
+            android:background="?android:attr/selectableItemBackground"
+        />
+
+        <ImageButton
+            android:id="@id/settings_button"
+            android:layout_width="@dimen/bubble_header_icon_size"
+            android:layout_height="@dimen/bubble_header_icon_size"
+            android:src="@drawable/ic_settings"
+            android:gravity="end|center_vertical"
+            android:scaleType="center"
+            android:tint="?android:attr/colorForeground"
+            android:background="?android:attr/selectableItemBackground"
+        />
+
+    </LinearLayout>
 
 </com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 098c975..53b3a7e 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -37,22 +37,16 @@
             android:id="@+id/keyguard_indication_enterprise_disclosure"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:gravity="center_horizontal"
-            android:textStyle="italic"
-            android:textColor="?attr/wallpaperTextColorSecondary"
-            android:textSize="16sp"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:gravity="center"
+            android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
             android:visibility="gone" />
 
         <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
             android:id="@+id/keyguard_indication_text"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:gravity="center_horizontal"
-            android:textStyle="italic"
-            android:textColor="?attr/wallpaperTextColorSecondary"
-            android:textSize="16sp"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:gravity="center"
+            android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
             android:accessibilityLiveRegion="polite" />
 
     </LinearLayout>
@@ -85,12 +79,13 @@
 
     <com.android.systemui.statusbar.phone.LockIcon
         android:id="@+id/lock_icon"
-        android:layout_width="@dimen/keyguard_affordance_width"
-        android:layout_height="@dimen/keyguard_affordance_height"
+        android:layout_width="@dimen/keyguard_lock_width"
+        android:layout_height="@dimen/keyguard_lock_height"
         android:layout_gravity="bottom|center_horizontal"
-        android:src="@drawable/ic_lock"
+        android:layout_marginBottom="@dimen/keyguard_lock_padding"
+        android:src="@*android:drawable/ic_lock_24dp"
         android:contentDescription="@string/accessibility_unlock_button"
-        android:scaleType="center" />
+        android:scaleType="fitCenter" />
 
     <FrameLayout
         android:id="@+id/overlay_container"
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
new file mode 100644
index 0000000..11d73a9
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -0,0 +1,33 @@
+precision mediump float;
+
+uniform sampler2D uTexture;
+uniform float uCenterReveal;
+uniform float uReveal;
+uniform float uAod2Opacity;
+uniform int uAodMode;
+varying vec2 vTextureCoordinates;
+
+vec3 luminosity(vec3 color) {
+    float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+    return vec3(lum);
+}
+
+vec4 transform(vec3 diffuse) {
+    // TODO: Add well comments here, tracking on b/123615467.
+    vec3 lum = luminosity(diffuse);
+    diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal));
+    float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal));
+    diffuse = smoothstep(val, 1.0, diffuse);
+    diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal));
+    return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
+}
+
+void main() {
+    vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
+    // TODO: Remove the branch logic here, tracking on b/123615467.
+    if (uAodMode != 0) {
+        gl_FragColor = transform(fragColor.rgb);
+    } else {
+        gl_FragColor = fragColor;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
new file mode 100644
index 0000000..4393e2b
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
@@ -0,0 +1,8 @@
+attribute vec4 aPosition;
+attribute vec2 aTextureCoordinates;
+varying vec2 vTextureCoordinates;
+
+void main() {
+    vTextureCoordinates = aTextureCoordinates;
+    gl_Position = aPosition;
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index baaf9d6..0fed96e 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -124,6 +124,7 @@
     <attr name="darkIconTheme" format="reference" />
     <attr name="wallpaperTextColor" format="reference|color" />
     <attr name="wallpaperTextColorSecondary" format="reference|color" />
+    <attr name="backgroundProtectedStyle" format="reference" />
 
     <declare-styleable name="SmartReplyView">
         <attr name="spacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d435c67..1060211 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -646,10 +646,15 @@
          + 8dp. -->
     <dimen name="assist_orb_navbar_scrim_height">56dp</dimen>
 
-    <!-- The width/height of the phone/camera/unlock icon view on keyguard. -->
+    <!-- The width/height of the phone/camera icon view on keyguard. -->
     <dimen name="keyguard_affordance_height">56dp</dimen>
     <dimen name="keyguard_affordance_width">56dp</dimen>
 
+    <!-- The width/height of the unlock icon view on keyguard. -->
+    <dimen name="keyguard_lock_height">32dp</dimen>
+    <dimen name="keyguard_lock_width">32dp</dimen>
+    <dimen name="keyguard_lock_padding">24dp</dimen>
+
     <dimen name="keyguard_indication_margin_bottom">65dp</dimen>
 
     <!-- The text size for battery level -->
@@ -1030,4 +1035,6 @@
     <dimen name="bubble_stack_offscreen">5dp</dimen>
     <!-- How far down the screen the stack starts. -->
     <dimen name="bubble_stack_starting_offset_y">100dp</dimen>
+    <!-- Size of image buttons in the bubble header -->
+    <dimen name="bubble_header_icon_size">48dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ffa5de8..56d9bf4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2309,7 +2309,7 @@
     <string name="ongoing_privacy_dialog_ok">Got it</string>
 
     <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
-    <string name="ongoing_privacy_dialog_open_settings">View details</string>
+    <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
 
     <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
     <string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
@@ -2346,4 +2346,12 @@
 
     <!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
     <string name="music_controls_no_title">No title</string>
+
+    <!-- Text used for content description of deep link button in the header of expanded bubble
+         view. [CHAR_LIMIT=NONE] -->
+    <string name="bubbles_deep_link_button_description">Open <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+    <!-- Text used for content description of settings button in the header of expanded bubble
+         view. [CHAR_LIMIT=NONE] -->
+    <string name="bubbles_settings_button_description">Open notification settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fa675b7..3f5b22e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -269,6 +269,7 @@
         <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
         <item name="passwordStyle">@style/PasswordTheme</item>
+        <item name="backgroundProtectedStyle">@style/BackgroundProtectedStyle</item>
 
         <!-- Needed for MediaRoute chooser dialog -->
         <item name="*android:isLightTheme">false</item>
@@ -285,6 +286,10 @@
         <item name="*android:isLightTheme">true</item>
     </style>
 
+    <style name="BackgroundProtectedStyle">
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
+    </style>
+
     <style name="LockPatternStyle">
         <item name="*android:regularColor">?attr/wallpaperTextColor</item>
         <item name="*android:successColor">?attr/wallpaperTextColor</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index f84c72e..1413ac1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -17,11 +17,13 @@
 package com.android.systemui.shared.recents.model;
 
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
 
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 
 /**
  * Data for a single thumbnail.
@@ -51,7 +53,9 @@
     }
 
     public ThumbnailData(TaskSnapshot snapshot) {
-        thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
+        thumbnail = Bitmap.wrapHardwareBuffer(
+                HardwareBuffer.createFromGraphicBuffer(snapshot.getSnapshot()),
+                snapshot.getColorSpace());
         insets = new Rect(snapshot.getContentInsets());
         orientation = snapshot.getOrientation();
         reducedResolution = snapshot.isReducedResolution();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bcd41a0..0970c21 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -99,6 +99,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.TimeZone;
+import java.util.function.Consumer;
 
 /**
  * Watches for updates that may be interesting to the keyguard, and provides
@@ -218,7 +219,8 @@
     // Battery status
     private BatteryStatus mBatteryStatus;
 
-    private final StrongAuthTracker mStrongAuthTracker;
+    @VisibleForTesting
+    protected StrongAuthTracker mStrongAuthTracker;
 
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
             mCallbacks = Lists.newArrayList();
@@ -344,8 +346,7 @@
                     handleUserUnlocked();
                     break;
                 case MSG_ASSISTANT_STACK_CHANGED:
-                    mAssistantVisible = (boolean)msg.obj;
-                    updateBiometricListeningState();
+                    setAssistantVisible((boolean) msg.obj);
                     break;
                 case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
                     updateBiometricListeningState();
@@ -354,7 +355,7 @@
                     updateLogoutEnabled();
                     break;
                 case MSG_TELEPHONY_CAPABLE:
-                    updateTelephonyCapable((boolean)msg.obj);
+                    updateTelephonyCapable((boolean) msg.obj);
                     break;
                 default:
                     super.handleMessage(msg);
@@ -538,7 +539,8 @@
         }
     }
 
-    private void onFingerprintAuthenticated(int userId) {
+    @VisibleForTesting
+    protected void onFingerprintAuthenticated(int userId) {
         Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
         mUserFingerprintAuthenticated.put(userId, true);
         // Update/refresh trust state only if user can skip bouncer
@@ -693,7 +695,8 @@
         }
     }
 
-    private void onFaceAuthenticated(int userId) {
+    @VisibleForTesting
+    protected void onFaceAuthenticated(int userId) {
         Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
         mUserFaceAuthenticated.put(userId, true);
         // Update/refresh trust state only if user can skip bouncer
@@ -898,8 +901,9 @@
 
 
     public boolean getUserCanSkipBouncer(int userId) {
-        return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
-                && isUnlockingWithBiometricAllowed());
+        boolean fingerprintOrFace = mUserFingerprintAuthenticated.get(userId)
+                || mUserFaceAuthenticated.get(userId);
+        return getUserHasTrust(userId) || (fingerprintOrFace && isUnlockingWithBiometricAllowed());
     }
 
     public boolean getUserHasTrust(int userId) {
@@ -950,6 +954,12 @@
         }
     }
 
+    @VisibleForTesting
+    void setAssistantVisible(boolean assistantVisible) {
+        mAssistantVisible = assistantVisible;
+        updateBiometricListeningState();
+    }
+
     static class DisplayClientState {
         public int clientGeneration;
         public boolean clearing;
@@ -1047,7 +1057,8 @@
         }
     };
 
-    private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+    @VisibleForTesting
+    protected final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
 
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1121,7 +1132,8 @@
         }
     };
 
-    private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+    @VisibleForTesting
+    FaceManager.AuthenticationCallback mFaceAuthenticationCallback
             = new FaceManager.AuthenticationCallback() {
 
         @Override
@@ -1298,9 +1310,13 @@
         }
     }
 
-    public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
-        public StrongAuthTracker(Context context) {
+    public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+        private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
+
+        public StrongAuthTracker(Context context,
+                Consumer<Integer> strongAuthRequiredChangedCallback) {
             super(context);
+            mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
         }
 
         public boolean isUnlockingWithBiometricAllowed() {
@@ -1316,7 +1332,7 @@
 
         @Override
         public void onStrongAuthRequiredChanged(int userId) {
-            notifyStrongAuthStateChanged(userId);
+            mStrongAuthRequiredChangedCallback.accept(userId);
         }
     }
 
@@ -1423,7 +1439,7 @@
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
-        mStrongAuthTracker = new StrongAuthTracker(context);
+        mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
 
         // Since device can't be un-provisioned, we only need to register a content observer
         // to update mDeviceProvisioned when we are...
@@ -1548,6 +1564,14 @@
         }
     }
 
+    /**
+     * Request passive authentication, when sensors detect that a user might be present.
+     */
+    public void onAuthInterruptDetected() {
+        if (DEBUG) Log.d(TAG, "onAuthInterruptDetected()");
+        updateFaceListeningState();
+    }
+
     private void updateFaceListeningState() {
         // If this message exists, we should not authenticate again until this message is
         // consumed by the handler
@@ -1585,11 +1609,13 @@
     }
 
     private boolean shouldListenForFace() {
-        return (mKeyguardIsVisible || !mDeviceInteractive ||
-                (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
-                shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
-                && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
-                && !mKeyguardGoingAway && !mFaceLockedOut && mFaceSettingEnabledForUser;
+        final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep;
+        final int user = getCurrentUser();
+
+        return (mBouncer || awakeKeyguard || shouldListenForFaceAssistant())
+                && !mSwitchingUser && !getUserCanSkipBouncer(user) && !isFaceDisabled(user)
+                && !mKeyguardGoingAway && !mFaceLockedOut && mFaceSettingEnabledForUser
+                && mUserManager.isUserUnlocked(user);
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index b461f69..04f887b 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -53,6 +53,15 @@
     }
 
     @Override
+    public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
+            int flags) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.startActivity(intent, onlyProvisioned, dismissShade, flags);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean dismissShade) {
         if (mActualStarter == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2aecc24..5040942 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -19,8 +19,11 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.RecordingCanvas;
@@ -28,7 +31,9 @@
 import android.graphics.RectF;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
+import android.opengl.GLSurfaceView;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
@@ -39,6 +44,7 @@
 import android.view.SurfaceHolder;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -51,12 +57,17 @@
 public class ImageWallpaper extends WallpaperService {
     private static final String TAG = "ImageWallpaper";
     private static final String GL_LOG_TAG = "ImageWallpaperGL";
+    // TODO: Testing purpose, need to remove later, b/123616712.
+    private static final String SENSOR_EVENT_AWAKE = "systemui.test.event.awake";
+    // TODO: Testing purpose, need to remove later, b/123616712.
+    private static final String SENSOR_EVENT_SLEEP = "systemui.test.event.sleep";
     private static final boolean DEBUG = false;
     private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
     private static final long DELAY_FORGET_WALLPAPER = 5000;
 
     private WallpaperManager mWallpaperManager;
     private DrawableEngine mEngine;
+    private GLEngine mGlEngine;
 
     @Override
     public void onCreate() {
@@ -73,10 +84,112 @@
 
     @Override
     public Engine onCreateEngine() {
-        mEngine = new DrawableEngine();
-        return mEngine;
+        mGlEngine = new GLEngine(this);
+        return mGlEngine;
     }
 
+    class GLEngine extends Engine {
+        private GLWallpaperSurfaceView mWallpaperSurfaceView;
+
+        GLEngine(Context context) {
+            mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
+            mWallpaperSurfaceView.setRenderer(
+                    new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
+            mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+            setOffsetNotificationsEnabled(true);
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
+            if (mWallpaperSurfaceView != null) {
+                mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode);
+            }
+        }
+
+        @Override
+        public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
+                float yOffsetStep, int xPixelOffset, int yPixelOffset) {
+            if (mWallpaperSurfaceView != null) {
+                mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
+            }
+        }
+
+        private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
+            private SensorEventListener mEventListener;
+            private WallpaperStatusListener mWallpaperChangedListener;
+
+            // TODO: Testing purpose, need to remove later, b/123616712.
+            /**
+             * For testing only: adb shell am broadcast -a <INTENT>
+             */
+            private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (intent == null) {
+                        return;
+                    }
+                    switch (intent.getAction()) {
+                        case SENSOR_EVENT_AWAKE:
+                            notifySensorEvents(true);
+                            break;
+                        case SENSOR_EVENT_SLEEP:
+                            notifySensorEvents(false);
+                            break;
+                    }
+                }
+            };
+
+            GLWallpaperSurfaceView(Context context) {
+                super(context);
+                setEGLContextClientVersion(2);
+                // TODO: Testing purpose, need to remove later, b/123616712.
+                if (Build.IS_DEBUGGABLE) {
+                    IntentFilter filter = new IntentFilter();
+                    filter.addAction(SENSOR_EVENT_AWAKE);
+                    filter.addAction(SENSOR_EVENT_SLEEP);
+                    registerReceiver(mReceiver, filter);
+                }
+            }
+
+            @Override
+            public SurfaceHolder getHolder() {
+                return getSurfaceHolder();
+            }
+
+            @Override
+            public void setRenderer(Renderer renderer) {
+                super.setRenderer(renderer);
+                mEventListener = (SensorEventListener) renderer;
+                mWallpaperChangedListener = (WallpaperStatusListener) renderer;
+            }
+
+            private void notifySensorEvents(boolean reach) {
+                if (mEventListener != null) {
+                    mEventListener.onSensorEvent(reach);
+                }
+            }
+
+            private void notifyAmbientModeChanged(boolean inAmbient) {
+                if (mWallpaperChangedListener != null) {
+                    mWallpaperChangedListener.onAmbientModeChanged(inAmbient);
+                }
+            }
+
+            private void notifyOffsetsChanged(float xOffset, float yOffset) {
+                if (mWallpaperChangedListener != null) {
+                    mWallpaperChangedListener.onOffsetsChanged(
+                            xOffset, yOffset, getHolder().getSurfaceFrame());
+                }
+            }
+
+            @Override
+            public void render() {
+                requestRender();
+            }
+        }
+    }
+
+    // TODO: Remove this engine, tracking on b/123617158.
     class DrawableEngine extends Engine {
         private final Runnable mUnloadWallpaperCallback = () -> {
             unloadWallpaper(false /* forgetSize */);
@@ -564,4 +677,46 @@
             }
         }
     }
+
+    /**
+     * A listener to trace sensor event.
+     */
+    public interface SensorEventListener {
+
+        /**
+         * Called back while sensor event comes.
+         * @param reach The status of sensor.
+         */
+        void onSensorEvent(boolean reach);
+    }
+
+    /**
+     * A listener to trace status of image wallpaper.
+     */
+    public interface WallpaperStatusListener {
+
+        /**
+         * Called back while ambient mode changes.
+         * @param inAmbientMode true if is in ambient mode, false otherwise.
+         */
+        void onAmbientModeChanged(boolean inAmbientMode);
+
+        /**
+         * Called back while wallpaper offsets.
+         * @param xOffset The offset portion along x.
+         * @param yOffset The offset portion along y.
+         */
+        void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
+    }
+
+    /**
+     * An abstraction for view of GLRenderer.
+     */
+    public interface ImageGLView {
+
+        /**
+         * Ask the view to render.
+         */
+        void render();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e3f6add..404f2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,6 +42,8 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.MainThread;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
@@ -76,11 +78,14 @@
     // Enables some subset of notifs to automatically become bubbles
     private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
 
-    // Secure settings
+    // Secure settings flags
+    // Feature level flag
+    private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
+    // Auto bubble flags set whether different notification types should be presented as a bubble
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
     private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
     private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
-    private static final String ENABLE_BUBBLE_ACTIVITY_VIEW = "experiment_bubble_activity_view";
+    // Use an activity view for an auto-bubbled notification if it has an appropriate content intent
     private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
 
     private final Context mContext;
@@ -262,8 +267,8 @@
                     R.layout.bubble_view, mStackView, false /* attachToRoot */);
             bubble.setNotif(notif);
             PendingIntent bubbleIntent = getValidBubbleIntent(notif);
-            if (shouldUseActivityView(mContext) || bubbleIntent != null) {
-                bubble.setBubbleIntent(getValidBubbleIntent(notif));
+            if (bubbleIntent != null) {
+                bubble.setBubbleIntent(bubbleIntent);
             }
             mBubbles.put(bubble.getKey(), bubble);
             mStackView.addBubble(bubble);
@@ -271,25 +276,12 @@
         updateVisibility();
     }
 
-    @Nullable
-    private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
-        Notification notification = notif.notification.getNotification();
-        if (canLaunchInActivityView(notification.getBubbleMetadata() != null
-                ? notification.getBubbleMetadata().getIntent() : null)) {
-            return notification.getBubbleMetadata().getIntent();
-        } else if (shouldUseContentIntent(mContext)
-                && canLaunchInActivityView(notification.contentIntent)) {
-            Log.d(TAG, "[addBubble " + notif.key
-                    + "]: No appOverlayIntent, using contentIntent.");
-            return notification.contentIntent;
-        }
-        Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
-        return null;
-    }
-
     /**
      * Removes the bubble associated with the {@param uri}.
+     * <p>
+     * Must be called from the main thread.
      */
+    @MainThread
     void removeBubble(String key) {
         BubbleView bv = mBubbles.remove(key);
         if (mStackView != null && bv != null) {
@@ -309,7 +301,10 @@
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
-            if (shouldAutoBubble(mContext, entry) || shouldBubble(entry)) {
+            if (!areBubblesEnabled(mContext)) {
+                return;
+            }
+            if (shouldAutoBubbleForFlags(mContext, entry) || shouldBubble(entry)) {
                 // TODO: handle group summaries
                 // It's a new notif, it shows in the shade and as a bubble
                 entry.setIsBubble(true);
@@ -320,6 +315,9 @@
         @Override
         public void onEntryInflated(NotificationEntry entry,
                 @NotificationInflater.InflationFlag int inflatedFlags) {
+            if (!areBubblesEnabled(mContext)) {
+                return;
+            }
             if (entry.isBubble() && mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
                 updateBubble(entry, true /* updatePosition */);
             }
@@ -327,6 +325,9 @@
 
         @Override
         public void onPreEntryUpdated(NotificationEntry entry) {
+            if (!areBubblesEnabled(mContext)) {
+                return;
+            }
             if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
                     && alertAgain(entry, entry.notification.getNotification())) {
                 entry.setShowInShadeWhenBubble(true);
@@ -342,6 +343,9 @@
         public void onEntryRemoved(NotificationEntry entry,
                 @Nullable NotificationVisibility visibility,
                 boolean removedByUser) {
+            if (!areBubblesEnabled(mContext)) {
+                return;
+            }
             entry.setShowInShadeWhenBubble(false);
             if (mBubbles.containsKey(entry.key)) {
                 mBubbles.get(entry.key).updateDotVisibility();
@@ -396,6 +400,30 @@
         return mTempRect;
     }
 
+    @VisibleForTesting
+    BubbleStackView getStackView() {
+        return mStackView;
+    }
+
+    @Nullable
+    private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
+        Notification notification = notif.notification.getNotification();
+        Notification.BubbleMetadata data = notif.getBubbleMetadata();
+        if (data != null && canLaunchInActivityView(data.getIntent())) {
+            return data.getIntent();
+        } else if (shouldUseContentIntent(mContext)
+                && canLaunchInActivityView(notification.contentIntent)) {
+            Log.d(TAG, "[addBubble " + notif.key
+                    + "]: No appOverlayIntent, using contentIntent.");
+            return notification.contentIntent;
+        }
+        Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
+        return null;
+    }
+
+    /**
+     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+     */
     private boolean canLaunchInActivityView(PendingIntent intent) {
         if (intent == null) {
             return false;
@@ -407,11 +435,6 @@
                 && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
     }
 
-    @VisibleForTesting
-    BubbleStackView getStackView() {
-        return mStackView;
-    }
-
     /**
      * Whether the notification has been developer configured to bubble and is allowed by the user.
      */
@@ -431,18 +454,14 @@
         boolean canChannelOverlay = channel != null && channel.canBubble();
         boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null
                 && n.getNotification().getBubbleMetadata().getIntent() != null;
-        return DEBUG_ENABLE_AUTO_BUBBLE && hasOverlayIntent && canChannelOverlay && canAppOverlay;
+        return hasOverlayIntent && canChannelOverlay && canAppOverlay;
     }
 
     /**
-     * Whether the notification should bubble or not. Gated by debug flag.
-     * <p>
-     * If a notification has been set to bubble via proper bubble APIs or if it is an important
-     * message-like notification.
-     * </p>
+     * Whether the notification should automatically bubble or not. Gated by secure settings flags.
      */
     @VisibleForTesting
-    protected boolean shouldAutoBubble(Context context, NotificationEntry entry) {
+    protected boolean shouldAutoBubbleForFlags(Context context, NotificationEntry entry) {
         if (entry.isBubbleDismissed()) {
             return false;
         }
@@ -469,11 +488,9 @@
         Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
         boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category);
         boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
-        boolean shouldAutoBubble =
-                (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
+        return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
                 || (isImportantOngoing && autoBubbleOngoing)
                 || autoBubbleAll;
-        return DEBUG_ENABLE_AUTO_BUBBLE && shouldAutoBubble;
     }
 
     private static boolean shouldAutoBubbleMessages(Context context) {
@@ -491,13 +508,13 @@
                 ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
 
-    private static boolean shouldUseActivityView(Context context) {
-        return Settings.Secure.getInt(context.getContentResolver(),
-                ENABLE_BUBBLE_ACTIVITY_VIEW, 0) != 0;
-    }
-
     private static boolean shouldUseContentIntent(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
     }
+
+    private static boolean areBubblesEnabled(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLES, 1) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index 71ae1f8..67b18fd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -17,31 +17,53 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.drawable.ShapeDrawable;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
+import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * Container for the expanded bubble view, handles rendering the caret and header of the view.
  */
-public class BubbleExpandedViewContainer extends LinearLayout {
+public class BubbleExpandedViewContainer extends LinearLayout implements View.OnClickListener {
+    private static final String TAG = "BubbleExpandedView";
 
     // The triangle pointing to the expanded view
     private View mPointerView;
     // The view displayed between the pointer and the expanded view
     private TextView mHeaderView;
+    // Tappable header icon deeplinking into the app
+    private ImageButton mDeepLinkIcon;
+    // Tappable header icon deeplinking into notification settings
+    private ImageButton mSettingsIcon;
     // The view that is being displayed for the expanded state
     private View mExpandedView;
 
+    private NotificationEntry mEntry;
+    private PackageManager mPm;
+    private String mAppName;
+
+    // Need reference to let it know to collapse when new task is launched
+    private BubbleStackView mStackView;
+
     public BubbleExpandedViewContainer(Context context) {
         this(context, null);
     }
@@ -57,7 +79,7 @@
     public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setOrientation(VERTICAL);
+        mPm = context.getPackageManager();
     }
 
     @Override
@@ -68,11 +90,85 @@
         mPointerView = findViewById(R.id.pointer_view);
         int width = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         int height = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
+
+        TypedArray ta = getContext().obtainStyledAttributes(
+                new int[] {android.R.attr.colorBackgroundFloating});
+        int bgColor = ta.getColor(0, Color.WHITE);
+        ta.recycle();
+
         ShapeDrawable triangleDrawable = new ShapeDrawable(
                 TriangleShape.create(width, height, true /* pointUp */));
-        triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
+        triangleDrawable.setTint(bgColor);
         mPointerView.setBackground(triangleDrawable);
-        mHeaderView = findViewById(R.id.bubble_content_header);
+
+        mHeaderView = findViewById(R.id.header_text);
+        mDeepLinkIcon = findViewById(R.id.deep_link_button);
+        mSettingsIcon = findViewById(R.id.settings_button);
+        mDeepLinkIcon.setOnClickListener(this);
+        mSettingsIcon.setOnClickListener(this);
+    }
+
+    /**
+     * Sets the notification entry used to populate this view.
+     */
+    public void setEntry(NotificationEntry entry, BubbleStackView stackView) {
+        mStackView = stackView;
+        mEntry = entry;
+
+        ApplicationInfo info;
+        try {
+            info = mPm.getApplicationInfo(
+                    entry.notification.getPackageName(),
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                mAppName = String.valueOf(mPm.getApplicationLabel(info));
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Ahh... just use package name
+            mAppName = entry.notification.getPackageName();
+        }
+
+        updateHeaderView();
+    }
+
+    private void updateHeaderView() {
+        mSettingsIcon.setContentDescription(getResources().getString(
+                R.string.bubbles_settings_button_description, mAppName));
+        mDeepLinkIcon.setContentDescription(getResources().getString(
+                R.string.bubbles_deep_link_button_description, mAppName));
+        if (mEntry != null && mEntry.getBubbleMetadata() != null) {
+            setHeaderText(mEntry.getBubbleMetadata().getTitle());
+        } else {
+            // This should only happen if we're auto-bubbling notification content that isn't
+            // explicitly a bubble
+            setHeaderText(mAppName);
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (mEntry == null) {
+            return;
+        }
+        Notification n = mEntry.notification.getNotification();
+        int id = view.getId();
+        if (id == R.id.deep_link_button) {
+            mStackView.collapseStack(() -> {
+                try {
+                    n.contentIntent.send();
+                } catch (PendingIntent.CanceledException e) {
+                    Log.w(TAG, "Failed to send intent for bubble with key: "
+                            + (mEntry != null ? mEntry.key : " null entry"));
+                }
+            });
+        } else if (id == R.id.settings_button) {
+            Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
+                    mEntry.notification.getUid());
+            mStackView.collapseStack(() -> mContext.startActivity(intent));
+        }
     }
 
     /**
@@ -87,7 +183,7 @@
     /**
      * Set the text displayed within the header.
      */
-    public void setHeaderText(CharSequence text) {
+    private void setHeaderText(CharSequence text) {
         mHeaderView.setText(text);
         mHeaderView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
     }
@@ -115,4 +211,13 @@
     public View getExpandedView() {
         return mExpandedView;
     }
+
+    private Intent getSettingsIntent(String packageName, final int appUid) {
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 8731bd5..b6acd63 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -20,7 +20,6 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import android.app.ActivityView;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -28,7 +27,9 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.util.StatsLog;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -55,6 +56,9 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
 /**
  * Renders bubbles in a stack and handles animating expanded and collapsed states.
  */
@@ -237,16 +241,24 @@
      * Sets the bubble that should be expanded and expands if needed.
      */
     public void setExpandedBubble(BubbleView bubbleToExpand) {
+        if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) {
+            // Previously expanded, notify that this bubble is no longer expanded
+            notifyExpansionChanged(mExpandedBubble, false /* expanded */);
+        }
+        BubbleView prevBubble = mExpandedBubble;
         mExpandedBubble = bubbleToExpand;
         if (!mIsExpanded) {
             // If we weren't previously expanded we should animate open.
             animateExpansion(true /* expand */);
+            logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
         } else {
             // Otherwise just update the views
             // TODO: probably animate / page to expanded one
             updateExpandedBubble();
             updatePointerPosition();
             requestUpdate();
+            logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+            logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
         }
         mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
         notifyExpansionChanged(mExpandedBubble, true /* expanded */);
@@ -275,6 +287,7 @@
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
         ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
         requestUpdate();
+        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
 
     /**
@@ -283,20 +296,24 @@
     public void removeBubble(BubbleView bubbleView) {
         int removedIndex = mBubbleContainer.indexOfChild(bubbleView);
         mBubbleContainer.removeView(bubbleView);
-        boolean wasExpanded = mIsExpanded;
         int bubbleCount = mBubbleContainer.getChildCount();
-        if (mIsExpanded && bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
+        if (bubbleCount == 0) {
+            // If no bubbles remain, collapse the entire stack.
+            collapseStack();
+            return;
+        } else if (bubbleView.equals(mExpandedBubble)) {
+            // Was the current bubble just removed?
             // If we have other bubbles and are expanded go to the next one or previous
             // if the bubble removed was last
             int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
             BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
-            setExpandedBubble(expandedBubble);
+            if (mIsExpanded) {
+                setExpandedBubble(expandedBubble);
+            } else {
+                mExpandedBubble = null;
+            }
         }
-        mIsExpanded = wasExpanded && mBubbleContainer.getChildCount() > 0;
-        if (wasExpanded != mIsExpanded) {
-            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
-        }
-        requestUpdate();
+        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
     }
 
     /**
@@ -305,6 +322,8 @@
     public void stackDismissed() {
         collapseStack();
         mBubbleContainer.removeAllViews();
+        logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
+                StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
     }
 
     /**
@@ -320,8 +339,7 @@
         if (updatePosition && !mIsExpanded) {
             // If alerting it gets promoted to top of the stack.
             if (mBubbleContainer.indexOfChild(bubbleView) != 0) {
-                mBubbleContainer.removeViewAndThen(bubbleView,
-                        () -> mBubbleContainer.addView(bubbleView, 0));
+                mBubbleContainer.moveViewTo(bubbleView, 0);
             }
             requestUpdate();
         }
@@ -329,6 +347,7 @@
             entry.setShowInShadeWhenBubble(false);
             requestUpdate();
         }
+        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
     }
 
     /**
@@ -364,18 +383,24 @@
             // TODO: Save opened bubble & move it to top of stack
             animateExpansion(false /* shouldExpand */);
             notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+            logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         }
     }
 
+    void collapseStack(Runnable endRunnable) {
+        collapseStack();
+        // TODO - use the runnable at end of animation
+        endRunnable.run();
+    }
+
     /**
      * Expands the stack fo bubbles.
      */
     public void expandStack() {
         if (!mIsExpanded) {
             mExpandedBubble = getTopBubble();
-            mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
-            animateExpansion(true /* shouldExpand */);
-            notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+            setExpandedBubble(mExpandedBubble);
+            logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
         }
     }
 
@@ -568,6 +593,9 @@
                         .setStiffness(SpringForce.STIFFNESS_LOW)
                         .setDampingRatio(SPRING_DAMPING_RATIO),
                 /* destination */ null);
+
+        logBubbleEvent(null /* no bubble associated with bubble stack move */,
+                StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
     }
 
     /**
@@ -627,6 +655,7 @@
             return;
         }
 
+        mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this);
         if (mExpandedBubble.hasAppOverlayIntent()) {
             // Bubble with activity view expanded state
             ActivityView expandedView = mExpandedBubble.getActivityView();
@@ -634,8 +663,6 @@
             expandedView.setLayoutParams(new LinearLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
 
-            final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
-            mExpandedViewContainer.setHeaderText(intent.getIntent().getComponent().toShortString());
             mExpandedViewContainer.setExpandedView(expandedView);
         } else {
             // Bubble with notification view expanded state
@@ -649,8 +676,6 @@
             } else {
                 mExpandedViewContainer.setExpandedView(null);
             }
-            // Bubble with notification as expanded state doesn't need a header / title
-            mExpandedViewContainer.setHeaderText(null);
         }
     }
 
@@ -730,4 +755,72 @@
         viewState.headsUpIsVisible = false;
         viewState.applyToView(view);
     }
+
+    /**
+     * @return the number of bubbles in the stack view.
+     */
+    private int getBubbleCount() {
+        return mBubbleContainer.getChildCount();
+    }
+
+    /**
+     * Finds the bubble index within the stack.
+     *
+     * @param bubbleView the view of the bubble.
+     * @return the index of the bubble view within the bubble stack. The range of the position
+     * is between 0 and the bubble count minus 1.
+     */
+    private int getBubbleIndex(BubbleView bubbleView) {
+        return mBubbleContainer.indexOfChild(bubbleView);
+    }
+
+    /**
+     * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
+     */
+    private float getNormalizedXPosition() {
+        return new BigDecimal(getPosition().x / mDisplaySize.x)
+                .setScale(4, RoundingMode.CEILING.HALF_UP)
+                .floatValue();
+    }
+
+    /**
+     * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
+     */
+    private float getNormalizedYPosition() {
+        return new BigDecimal(getPosition().y / mDisplaySize.y)
+                .setScale(4, RoundingMode.CEILING.HALF_UP)
+                .floatValue();
+    }
+
+    /**
+     * Logs the bubble UI event.
+     *
+     * @param bubble the bubble that is being interacted on. Null value indicates that
+     *               the user interaction is not specific to one bubble.
+     * @param action the user interaction enum.
+     */
+    private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+        if (bubble == null) {
+            StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+                    null /* package name */,
+                    null /* notification channel */,
+                    0 /* notification ID */,
+                    0 /* bubble position */,
+                    getBubbleCount(),
+                    action,
+                    getNormalizedXPosition(),
+                    getNormalizedYPosition());
+        } else {
+            StatusBarNotification notification = bubble.getEntry().notification;
+            StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+                    notification.getPackageName(),
+                    notification.getNotification().getChannelId(),
+                    notification.getId(),
+                    getBubbleIndex(bubble),
+                    getBubbleCount(),
+                    action,
+                    getNormalizedXPosition(),
+                    getNormalizedYPosition());
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index dc94832..2c23c0c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -37,6 +37,7 @@
 import android.widget.TextView;
 
 import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -59,6 +60,7 @@
 
     private NotificationEntry mEntry;
     private PendingIntent mAppOverlayIntent;
+    private BubbleController mBubbleController;
     private ActivityView mActivityView;
     private boolean mActivityViewReady;
     private boolean mActivityViewStarted;
@@ -81,6 +83,7 @@
         // XXX: can this padding just be on the view and we look it up?
         mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
         mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
+        mBubbleController = Dependency.get(BubbleController.class);
     }
 
     @Override
@@ -248,6 +251,20 @@
                 public void onActivityViewDestroyed(ActivityView view) {
                     mActivityViewReady = false;
                 }
+
+                /**
+                 * This is only called for tasks on this ActivityView, which is also set to
+                 * single-task mode -- meaning never more than one task on this display. If a task
+                 * is being removed, it's the top Activity finishing and this bubble should
+                 * be removed or collapsed.
+                 */
+                @Override
+                public void onTaskRemovalStarted(int taskId) {
+                    if (mEntry != null) {
+                        // Must post because this is called from a binder thread.
+                        post(() -> mBubbleController.removeBubble(mEntry.key));
+                    }
+                }
             });
         }
         return mActivityView;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index 1ced3a4..e4e6bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -27,9 +27,8 @@
 
 import com.android.systemui.R;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -122,12 +121,8 @@
     protected final HashMap<DynamicAnimation.ViewProperty, DynamicAnimation.OnAnimationEndListener>
             mEndListenerForProperty = new HashMap<>();
 
-    /**
-     * List of views that were passed to removeView, but are currently being animated out. These
-     * views will be actually removed by the controller (via super.removeView) once they're done
-     * animating out.
-     */
-    private final List<View> mViewsToBeActuallyRemoved = new ArrayList<>();
+    /** Set of currently rendered transient views. */
+    private final Set<View> mTransientViews = new HashSet<>();
 
     /** The currently active animation controller. */
     private PhysicsAnimationController mController;
@@ -186,23 +181,6 @@
         mEndListenerForProperty.remove(property);
     }
 
-    /**
-     * Returns the index of the view that precedes the given index, ignoring views that were passed
-     * to removeView, but are currently being animated out before actually being removed.
-     *
-     * @return index of the preceding view, or -1 if there are none.
-     */
-    public int getPrecedingNonRemovedViewIndex(int index) {
-        for (int i = index + 1; i < getChildCount(); i++) {
-            View precedingView = getChildAt(i);
-            if (!mViewsToBeActuallyRemoved.contains(precedingView)) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         super.addView(child, index, params);
@@ -224,6 +202,24 @@
         removeViewAndThen(view, /* callback */ null);
     }
 
+    @Override
+    public void addTransientView(View view, int index) {
+        super.addTransientView(view, index);
+        mTransientViews.add(view);
+    }
+
+    @Override
+    public void removeTransientView(View view) {
+        super.removeTransientView(view);
+        mTransientViews.remove(view);
+    }
+
+    /** Immediately moves the view from wherever it currently is, to the given index. */
+    public void moveViewTo(View view, int index) {
+        super.removeView(view);
+        addView(view, index);
+    }
+
     /**
      * Let the controller know that this view should be removed, and then call the callback once the
      * controller has finished any removal animations and the view has actually been removed.
@@ -231,23 +227,31 @@
     public void removeViewAndThen(View view, Runnable callback) {
         if (mController != null) {
             final int index = indexOfChild(view);
-            // Remove the view only if it exists in this layout, and we're not already working on
-            // animating its removal.
-            if (index > -1 && !mViewsToBeActuallyRemoved.contains(view)) {
-                mViewsToBeActuallyRemoved.add(view);
-                setChildrenVisibility();
 
-                // Tell the controller to animate this view out, and call the callback when it wants
-                // to actually remove the view.
-                mController.onChildToBeRemoved(view, index, () -> {
-                    removeViewImmediateAndThen(view, callback);
-                    mViewsToBeActuallyRemoved.remove(view);
-                });
-            }
+            // Remove the view and add it back as a transient view so we can animate it out.
+            super.removeView(view);
+            addTransientView(view, index);
+
+            setChildrenVisibility();
+
+            // Tell the controller to animate this view out, and call the callback when it's
+            // finished.
+            mController.onChildToBeRemoved(view, index, () -> {
+                // Done animating, remove the transient view.
+                removeTransientView(view);
+
+                if (callback != null) {
+                    callback.run();
+                }
+            });
         } else {
             // Without a controller, nobody will animate this view out, so it gets an unceremonious
             // departure.
-            removeViewImmediateAndThen(view, callback);
+            super.removeView(view);
+
+            if (callback != null) {
+                callback.run();
+            }
         }
     }
 
@@ -278,17 +282,18 @@
     }
 
     /**
-     * Animates the property of the child at the given index to the given value, then runs the
-     * callback provided when the animation ends.
+     * Animates the property of the given child view, then runs the callback provided when the
+     * animation ends.
      */
-    protected void animateValueForChildAtIndex(
+    protected void animateValueForChild(
             DynamicAnimation.ViewProperty property,
-            int index,
+            View view,
             float value,
             float startVel,
             Runnable after) {
-        if (index < getChildCount()) {
-            final SpringAnimation animation = getAnimationAtIndex(property, index);
+        if (view != null) {
+            final SpringAnimation animation =
+                    (SpringAnimation) view.getTag(getTagIdForProperty(property));
             if (after != null) {
                 animation.addEndListener(new OneTimeEndListener() {
                     @Override
@@ -300,14 +305,38 @@
                 });
             }
 
-            if (startVel != Float.MAX_VALUE) {
-                animation.setStartVelocity(startVel);
-            }
-
             animation.animateToFinalPosition(value);
         }
     }
 
+    protected void animateValueForChild(
+            DynamicAnimation.ViewProperty property,
+            View view,
+            float value,
+            Runnable after) {
+        animateValueForChild(property, view, value, Float.MAX_VALUE, after);
+    }
+
+    protected void animateValueForChild(
+            DynamicAnimation.ViewProperty property,
+            View view,
+            float value) {
+        animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null);
+    }
+
+    /**
+     * Animates the property of the child at the given index to the given value, then runs the
+     * callback provided when the animation ends.
+     */
+    protected void animateValueForChildAtIndex(
+            DynamicAnimation.ViewProperty property,
+            int index,
+            float value,
+            float startVel,
+            Runnable after) {
+        animateValueForChild(property, getChildAt(index), value, startVel, after);
+    }
+
     /** Shortcut to animate a value with a callback, but no start velocity. */
     protected void animateValueForChildAtIndex(
             DynamicAnimation.ViewProperty property,
@@ -366,18 +395,6 @@
         }
     }
 
-
-    /** Immediately removes the view, without notifying the controller, then runs the callback. */
-    private void removeViewImmediateAndThen(View view, Runnable callback) {
-        super.removeView(view);
-
-        if (callback != null) {
-            callback.run();
-        }
-
-        setChildrenVisibility();
-    }
-
     /**
      * Retrieves the animation of the given property from the view at the given index via the view
      * tag system.
@@ -401,7 +418,11 @@
         newAnim.addUpdateListener((animation, value, velocity) -> {
             final int nextAnimInChain =
                     mController.getNextAnimationInChain(property, indexOfChild(child));
-            if (nextAnimInChain == PhysicsAnimationController.NONE) {
+
+            // If the controller doesn't want us to chain, or if we're a transient view in the
+            // process of being removed, don't chain.
+            if (nextAnimInChain == PhysicsAnimationController.NONE
+                    || mTransientViews.contains(child)) {
                 return;
             }
 
@@ -412,9 +433,7 @@
             // If this property's animations should be chained, then check to see if there is a
             // subsequent animation within the rendering limit, and if so, tell it to animate to
             // this animation's new value (plus the offset).
-            if (nextAnimInChain < Math.min(
-                    getChildCount(),
-                    mMaxRenderedChildren + mViewsToBeActuallyRemoved.size())) {
+            if (nextAnimInChain < Math.min(getChildCount(), mMaxRenderedChildren)) {
                 getAnimationAtIndex(property, animIndex + 1)
                         .animateToFinalPosition(value + offset);
             } else if (nextAnimInChain < getChildCount()) {
@@ -442,9 +461,7 @@
                     // Ignore views that are animating out when calculating whether to hide the
                     // view. That is, if we're supposed to render 5 views, but 4 are animating out
                     // and will soon be removed, render up to 9 views temporarily.
-                    i < (mMaxRenderedChildren + mViewsToBeActuallyRemoved.size())
-                        ? View.VISIBLE
-                        : View.GONE);
+                    i < mMaxRenderedChildren ? View.VISIBLE : View.GONE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 0f51376..23c6fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -53,8 +53,8 @@
     /** Scale factor to use initially for new bubbles being animated in. */
     private static final float ANIMATE_IN_STARTING_SCALE = 1.15f;
 
-    /** Translation factor (multiplied by stack offset) to use for new bubbles being animated in. */
-    private static final int ANIMATE_IN_TRANSLATION_FACTOR = 4;
+    /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */
+    private static final int ANIMATE_TRANSLATION_FACTOR = 4;
 
     /**
      * Values to use for the default {@link SpringForce} provided to the physics animation layout.
@@ -309,7 +309,7 @@
             // animate in from this position. Since the animations are chained, when the new bubble
             // flies in from the side, it will push the other ones out of the way.
             float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
-            child.setTranslationX(mStackPosition.x - (ANIMATE_IN_TRANSLATION_FACTOR * xOffset));
+            child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
             mLayout.animateValueForChildAtIndex(
                     DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
         }
@@ -318,27 +318,19 @@
     @Override
     void onChildToBeRemoved(View child, int index, Runnable actuallyRemove) {
         // Animate the child out, actually removing it once its alpha is zero.
-        mLayout.animateValueForChildAtIndex(
-                DynamicAnimation.ALPHA, index, 0f, () -> {
-                    actuallyRemove.run();
-                });
-        mLayout.animateValueForChildAtIndex(
-                DynamicAnimation.SCALE_X, index, ANIMATE_IN_STARTING_SCALE);
-        mLayout.animateValueForChildAtIndex(
-                DynamicAnimation.SCALE_Y, index, ANIMATE_IN_STARTING_SCALE);
+        mLayout.animateValueForChild(
+                DynamicAnimation.ALPHA, child, 0f, actuallyRemove);
+        mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_IN_STARTING_SCALE);
+        mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_IN_STARTING_SCALE);
 
-        final boolean hasPrecedingChild = index + 1 < mLayout.getChildCount();
-        if (hasPrecedingChild) {
-            final int precedingViewIndex = mLayout.getPrecedingNonRemovedViewIndex(index);
-            if (precedingViewIndex >= 0) {
-                final float offsetX =
-                        getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
-                mLayout.animatePositionForChildAtIndex(
-                        precedingViewIndex,
-                        mStackPosition.x + (index * offsetX),
-                        mStackPosition.y);
-            }
-        }
+        // Animate the removing view in the opposite direction of the stack.
+        final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+        mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_X, child,
+                mStackPosition.x - (-xOffset * ANIMATE_TRANSLATION_FACTOR));
+
+        // Pull the top of the stack to the correct position, the chained animations will instruct
+        // any children that are out of place to animate to the correct position.
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
     }
 
     /** Moves the stack, without any animation, to the starting position. */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index a784773..9bca3c7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,19 +35,19 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int REASONS = 10;
+    public static final int REASONS = 10;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
     public static final int PULSE_REASON_NOTIFICATION = 1;
     public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
-    public static final int PULSE_REASON_SENSOR_PICKUP = 3;
-    public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
+    public static final int REASON_SENSOR_PICKUP = 3;
+    public static final int REASON_SENSOR_DOUBLE_TAP = 4;
     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
     public static final int PULSE_REASON_DOCKING = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
     public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
-    public static final int PULSE_REASON_SENSOR_TAP = 9;
+    public static final int REASON_SENSOR_TAP = 9;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -202,13 +202,13 @@
             case PULSE_REASON_INTENT: return "intent";
             case PULSE_REASON_NOTIFICATION: return "notification";
             case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
-            case PULSE_REASON_SENSOR_PICKUP: return "pickup";
-            case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+            case REASON_SENSOR_PICKUP: return "pickup";
+            case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
             case PULSE_REASON_DOCKING: return "docking";
             case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
-            case PULSE_REASON_SENSOR_TAP: return "tap";
+            case REASON_SENSOR_TAP: return "tap";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 562edd6..5efdc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -97,20 +97,20 @@
                         mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
                         Settings.Secure.DOZE_PICK_UP_GESTURE,
                         config.dozePickupSensorAvailable(),
-                        DozeLog.PULSE_REASON_SENSOR_PICKUP, false /* touchCoords */,
+                        DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
                         false /* touchscreen */),
                 new TriggerSensor(
                         findSensorWithType(config.doubleTapSensorType()),
                         Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
                         true /* configured */,
-                        DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
                         dozeParameters.doubleTapReportsTouchCoordinates(),
                         true /* touchscreen */),
                 new TriggerSensor(
                         findSensorWithType(config.tapSensorType()),
                         Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
                         true /* configured */,
-                        DozeLog.PULSE_REASON_SENSOR_TAP,
+                        DozeLog.REASON_SENSOR_TAP,
                         false /* reports touch coordinates */,
                         true /* touchscreen */),
                 new TriggerSensor(
@@ -130,7 +130,7 @@
                         false /* touchscreen */),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
-                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                        Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
                         mConfig.wakeScreenGestureAvailable(),
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
@@ -530,7 +530,7 @@
 
         /**
          * Called when a sensor requests a pulse
-         * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
+         * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
          * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
          * @param screenX the location on the screen where the sensor fired or -1
  *                if the sensor doesn't support reporting screen locations.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index dc505b5..8a35e70 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -108,10 +108,6 @@
         DozeLog.traceNotificationPulse(mContext);
     }
 
-    private void onWhisper() {
-        requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
-    }
-
     private void proximityCheckThenCall(IntConsumer callback,
             boolean alreadyPerformedProxCheck,
             int reason) {
@@ -137,9 +133,9 @@
     @VisibleForTesting
     void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
             float screenX, float screenY, float[] rawValues) {
-        boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
-        boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP;
-        boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+        boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
+        boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
+        boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
         boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
new file mode 100644
index 0000000..d03b00b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
+import static android.opengl.GLES20.GL_VERTEX_SHADER;
+import static android.opengl.GLES20.glAttachShader;
+import static android.opengl.GLES20.glCompileShader;
+import static android.opengl.GLES20.glCreateProgram;
+import static android.opengl.GLES20.glCreateShader;
+import static android.opengl.GLES20.glGetAttribLocation;
+import static android.opengl.GLES20.glGetUniformLocation;
+import static android.opengl.GLES20.glLinkProgram;
+import static android.opengl.GLES20.glShaderSource;
+import static android.opengl.GLES20.glUseProgram;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
+ */
+class ImageGLProgram {
+    private static final String TAG = ImageGLProgram.class.getSimpleName();
+
+    private Context mContext;
+    private int mProgramHandle;
+
+    ImageGLProgram(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    private int loadShaderProgram(int vertexId, int fragmentId) {
+        final String vertexSrc = getShaderResource(vertexId);
+        final String fragmentSrc = getShaderResource(fragmentId);
+        final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
+        final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
+        return getProgramHandle(vertexHandle, fragmentHandle);
+    }
+
+    private String getShaderResource(int shaderId) {
+        Resources res = mContext.getResources();
+        StringBuilder code = new StringBuilder();
+
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(res.openRawResource(shaderId)))) {
+            String nextLine;
+            while ((nextLine = reader.readLine()) != null) {
+                code.append(nextLine).append("\n");
+            }
+        } catch (IOException | Resources.NotFoundException ex) {
+            Log.d(TAG, "Can not read the shader source", ex);
+            code = null;
+        }
+
+        return code == null ? "" : code.toString();
+    }
+
+    private int getShaderHandle(int type, String src) {
+        final int shader = glCreateShader(type);
+        if (shader == 0) {
+            Log.d(TAG, "Create shader failed, type=" + type);
+            return 0;
+        }
+        glShaderSource(shader, src);
+        glCompileShader(shader);
+        return shader;
+    }
+
+    private int getProgramHandle(int vertexHandle, int fragmentHandle) {
+        final int program = glCreateProgram();
+        if (program == 0) {
+            Log.d(TAG, "Can not create OpenGL ES program");
+            return 0;
+        }
+
+        glAttachShader(program, vertexHandle);
+        glAttachShader(program, fragmentHandle);
+        glLinkProgram(program);
+        return program;
+    }
+
+    boolean useGLProgram(int vertexResId, int fragmentResId) {
+        mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
+        glUseProgram(mProgramHandle);
+        return true;
+    }
+
+    int getAttributeHandle(String name) {
+        return glGetAttribLocation(mProgramHandle, name);
+    }
+
+    int getUniformHandle(String name) {
+        return glGetUniformLocation(mProgramHandle, name);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
new file mode 100644
index 0000000..4e07872
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_FLOAT;
+import static android.opengl.GLES20.GL_LINEAR;
+import static android.opengl.GLES20.GL_TEXTURE0;
+import static android.opengl.GLES20.GL_TEXTURE_2D;
+import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
+import static android.opengl.GLES20.GL_TRIANGLES;
+import static android.opengl.GLES20.glActiveTexture;
+import static android.opengl.GLES20.glBindTexture;
+import static android.opengl.GLES20.glDrawArrays;
+import static android.opengl.GLES20.glEnableVertexAttribArray;
+import static android.opengl.GLES20.glGenTextures;
+import static android.opengl.GLES20.glTexParameteri;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glVertexAttribPointer;
+
+import android.graphics.Bitmap;
+import android.opengl.GLUtils;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * This class takes charge of the geometry data like vertices and texture coordinates.
+ * It delivers these data to opengl runtime and triggers draw calls if necessary.
+ */
+class ImageGLWallpaper {
+    private static final String TAG = ImageGLWallpaper.class.getSimpleName();
+
+    static final String A_POSITION = "aPosition";
+    static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
+    static final String U_CENTER_REVEAL = "uCenterReveal";
+    static final String U_REVEAL = "uReveal";
+    static final String U_AOD2OPACITY = "uAod2Opacity";
+    static final String U_TEXTURE = "uTexture";
+    static final String U_AOD_MODE = "uAodMode";
+
+    private static final int HANDLE_UNDEFINED = -1;
+    private static final int POSITION_COMPONENT_COUNT = 2;
+    private static final int TEXTURE_COMPONENT_COUNT = 2;
+    private static final int BYTES_PER_FLOAT = 4;
+
+    // Vertices to define the square with 2 triangles.
+    private static final float[] VERTICES = {
+            -1.0f,  -1.0f,
+            +1.0f,  -1.0f,
+            +1.0f,  +1.0f,
+            +1.0f,  +1.0f,
+            -1.0f,  +1.0f,
+            -1.0f,  -1.0f
+    };
+
+    // Texture coordinates that maps to vertices.
+    private static final float[] TEXTURES = {
+            0f, 1f,
+            1f, 1f,
+            1f, 0f,
+            1f, 0f,
+            0f, 0f,
+            0f, 1f
+    };
+
+    private final FloatBuffer mVertexBuffer;
+    private final FloatBuffer mTextureBuffer;
+    private final ImageGLProgram mProgram;
+
+    private int mAttrPosition;
+    private int mAttrTextureCoordinates;
+    private int mUniAod2Opacity;
+    private int mUniAodMode;
+    private int mUniCenterReveal;
+    private int mUniReveal;
+    private int mUniTexture;
+    private int mTextureId;
+
+    ImageGLWallpaper(ImageGLProgram program) {
+        mProgram = program;
+
+        // Create an float array in opengles runtime (native) and put vertex data.
+        mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT)
+            .order(ByteOrder.nativeOrder())
+            .asFloatBuffer();
+        mVertexBuffer.put(VERTICES);
+        mVertexBuffer.position(0);
+
+        // Create an float array in opengles runtime (native) and put texture data.
+        mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT)
+            .order(ByteOrder.nativeOrder())
+            .asFloatBuffer();
+        mTextureBuffer.put(TEXTURES);
+        mTextureBuffer.position(0);
+    }
+
+    void setup() {
+        setupAttributes();
+        setupUniforms();
+    }
+
+    private void setupAttributes() {
+        mAttrPosition = mProgram.getAttributeHandle(A_POSITION);
+        mVertexBuffer.position(0);
+        glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT,
+                false, 0, mVertexBuffer);
+        glEnableVertexAttribArray(mAttrPosition);
+
+        mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES);
+        mTextureBuffer.position(0);
+        glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT,
+                false, 0, mTextureBuffer);
+        glEnableVertexAttribArray(mAttrTextureCoordinates);
+    }
+
+    private void setupUniforms() {
+        mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
+        mUniAodMode = mProgram.getUniformHandle(U_AOD_MODE);
+        mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL);
+        mUniReveal = mProgram.getUniformHandle(U_REVEAL);
+        mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
+    }
+
+    int getHandle(String name) {
+        switch (name) {
+            case A_POSITION:
+                return mAttrPosition;
+            case A_TEXTURE_COORDINATES:
+                return mAttrTextureCoordinates;
+            case U_AOD2OPACITY:
+                return mUniAod2Opacity;
+            case U_AOD_MODE:
+                return mUniAodMode;
+            case U_CENTER_REVEAL:
+                return mUniCenterReveal;
+            case U_REVEAL:
+                return mUniReveal;
+            case U_TEXTURE:
+                return mUniTexture;
+            default:
+                return HANDLE_UNDEFINED;
+        }
+    }
+
+    void draw() {
+        glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
+    }
+
+    void setupTexture(Bitmap bitmap) {
+        final int[] tids = new int[1];
+
+        if (bitmap == null) {
+            Log.w(TAG, "setupTexture: invalid bitmap");
+            return;
+        }
+
+        // Generate one texture object and store the id in tids[0].
+        glGenTextures(1, tids, 0);
+        if (tids[0] == 0) {
+            Log.w(TAG, "setupTexture: glGenTextures() failed");
+            return;
+        }
+
+        // Bind a named texture to a texturing target.
+        glBindTexture(GL_TEXTURE_2D, tids[0]);
+        // Load the bitmap data and copy it over into the texture object that is currently bound.
+        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+        // Use bilinear texture filtering when minification.
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        // Use bilinear texture filtering when magnification.
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        mTextureId = tids[0];
+    }
+
+    void useTexture() {
+        // Set the active texture unit to texture unit 0.
+        glActiveTexture(GL_TEXTURE0);
+        // Bind the texture to this unit.
+        glBindTexture(GL_TEXTURE_2D, mTextureId);
+        // Let the texture sampler in fragment shader to read form this texture unit.
+        glUniform1i(mUniTexture, 0);
+    }
+
+    void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight,
+            float xOffset, float yOffset) {
+        if (bitmap == null) {
+            Log.d(TAG, "adjustTextureCoordinates: invalid bitmap");
+            return;
+        }
+
+        float ratioW = 1f;
+        float ratioH = 1f;
+        int bitmapWidth = bitmap.getWidth();
+        int bitmapHeight = bitmap.getHeight();
+
+        boolean adjustWidth = bitmapWidth > surfaceWidth;
+        if (adjustWidth) {
+            ratioW = (float) surfaceWidth / bitmapWidth;
+            float referenceX = xOffset + ratioW > 1f ? 1f - ratioW : xOffset;
+            for (int i = 0; i < TEXTURES.length; i += 2) {
+                if (i == 2 || i == 4 || i == 6) {
+                    TEXTURES[i] = Math.min(1f, referenceX + ratioW);
+                } else {
+                    TEXTURES[i] = referenceX;
+                }
+            }
+        }
+
+        boolean adjustHeight = bitmapHeight > surfaceHeight;
+        if (adjustHeight) {
+            ratioH = (float) surfaceHeight / bitmapHeight;
+            float referenceY = yOffset + ratioH > 1f ? 1f - ratioH : yOffset;
+            for (int i = 1; i < TEXTURES.length; i += 2) {
+                if (i == 1 || i == 3 || i == 11) {
+                    TEXTURES[i] = Math.min(1f, referenceY + ratioH);
+                } else {
+                    TEXTURES[i] = referenceY;
+                }
+            }
+        }
+
+        if (adjustWidth || adjustHeight) {
+            mTextureBuffer.put(TEXTURES);
+            mTextureBuffer.position(0);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
new file mode 100644
index 0000000..477e7d7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.glwallpaper;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * A helper class that computes histogram and percentile 85 from a bitmap.
+ * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ */
+class ImageProcessHelper {
+    private static final String TAG = ImageProcessHelper.class.getSimpleName();
+    private static final float DEFAULT_PER85 = 0.8f;
+    private static final int MSG_UPDATE_PER85 = 1;
+
+    /**
+     * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
+     * Luminance = .2126f * r + .7152f * g + .0722f * b.
+     */
+    private static final float[] LUMINOSITY_MATRIX = new float[] {
+            .2126f,     .0000f,     .0000f,     .0000f,     .0000f,
+            .0000f,     .7152f,     .0000f,     .0000f,     .0000f,
+            .0000f,     .0000f,     .0722f,     .0000f,     .0000f,
+            .0000f,     .0000f,     .0000f,     1.000f,     .0000f
+    };
+
+    private final Handler mHandler = new Handler(new Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_PER85:
+                    mPer85 = (float) msg.obj;
+                    return true;
+                default:
+                    return false;
+            }
+        }
+    });
+
+    private float mPer85 = DEFAULT_PER85;
+
+    void startComputingPercentile85(Bitmap bitmap) {
+        new Per85ComputeTask(mHandler).execute(bitmap);
+    }
+
+    float getPercentile85() {
+        return mPer85;
+    }
+
+    private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+        private Handler mUpdateHandler;
+
+        Per85ComputeTask(Handler handler) {
+            super(handler);
+            mUpdateHandler = handler;
+        }
+
+        @Override
+        protected Float doInBackground(Bitmap... bitmaps) {
+            Bitmap bitmap = bitmaps[0];
+            if (bitmap != null) {
+                int[] histogram = processHistogram(bitmap);
+                return computePercentile85(bitmap, histogram);
+            }
+            Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
+            return DEFAULT_PER85;
+        }
+
+        @Override
+        protected void onPostExecute(Float result) {
+            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+            mUpdateHandler.sendMessage(msg);
+        }
+
+        private int[] processHistogram(Bitmap bitmap) {
+            int width = bitmap.getWidth();
+            int height = bitmap.getHeight();
+
+            Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
+            Canvas canvas = new Canvas(target);
+            ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
+            Paint paint = new Paint();
+            paint.setColorFilter(new ColorMatrixColorFilter(cm));
+            canvas.drawBitmap(bitmap, new Matrix(), paint);
+
+            // TODO: Fine tune the performance here, tracking on b/123615079.
+            int[] histogram = new int[256];
+            for (int row = 0; row < height; row++) {
+                for (int col = 0; col < width; col++) {
+                    int pixel = target.getPixel(col, row);
+                    int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
+                    histogram[y]++;
+                }
+            }
+
+            return histogram;
+        }
+
+        private float computePercentile85(Bitmap bitmap, int[] histogram) {
+            float per85 = DEFAULT_PER85;
+            int pixelCount = bitmap.getWidth() * bitmap.getHeight();
+            float[] acc = new float[256];
+            for (int i = 0; i < acc.length; i++) {
+                acc[i] = (float) histogram[i] / pixelCount;
+                float prev = i == 0 ? 0f : acc[i - 1];
+                float next = acc[i];
+                float idx = (float) (i + 1) / 255;
+                float sum = prev + next;
+                if (prev < 0.85f && sum >= 0.85f) {
+                    per85 = idx;
+                }
+                if (i > 0) {
+                    acc[i] += acc[i - 1];
+                }
+            }
+            return per85;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
new file mode 100644
index 0000000..787972c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.glwallpaper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.systemui.Interpolators;
+
+/**
+ * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
+ * The transition will happen while getting awake and quit events.
+ */
+class ImageRevealHelper {
+    private static final String TAG = ImageRevealHelper.class.getSimpleName();
+    private static final float MAX_REVEAL = 0f;
+    private static final float MIN_REVEAL = 1f;
+    private static final int REVEAL_DURATION = 1000;
+
+    private final ValueAnimator mAnimator;
+    private final RevealStateListener mRevealListener;
+    private float mReveal = MIN_REVEAL;
+    private boolean mAwake = false;
+
+    ImageRevealHelper(RevealStateListener listener) {
+        mRevealListener = listener;
+        mAnimator = ValueAnimator.ofFloat();
+        mAnimator.setDuration(REVEAL_DURATION);
+        mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mAnimator.addUpdateListener(animator -> {
+            mReveal = (float) animator.getAnimatedValue();
+            if (mRevealListener != null) {
+                mRevealListener.onRevealStateChanged();
+            }
+        });
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mIsCanceled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mIsCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mIsCanceled) {
+                    mAwake = !mAwake;
+                }
+                mIsCanceled = false;
+            }
+        });
+    }
+
+    private void animate() {
+        mAnimator.cancel();
+        mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
+        mAnimator.start();
+    }
+
+    public float getReveal() {
+        return mReveal;
+    }
+
+    public boolean isAwake() {
+        return mAwake;
+    }
+
+    void updateAwake(boolean awake) {
+        mAwake = awake;
+        animate();
+    }
+
+    void sleep() {
+        mReveal = MIN_REVEAL;
+        mAwake = false;
+    }
+
+    /**
+     * A listener to trace value changes of reveal.
+     */
+    public interface RevealStateListener {
+
+        /**
+         * Called back while reveal status changes.
+         */
+        void onRevealStateChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
new file mode 100644
index 0000000..8916b28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.glwallpaper;
+
+import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
+import static android.opengl.GLES20.glClear;
+import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glUniform1f;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glViewport;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.opengl.GLSurfaceView;
+import android.util.Log;
+
+import com.android.systemui.ImageWallpaper;
+import com.android.systemui.ImageWallpaper.ImageGLView;
+import com.android.systemui.R;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A GL renderer for image wallpaper.
+ */
+public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
+        ImageWallpaper.SensorEventListener, ImageWallpaper.WallpaperStatusListener,
+        ImageRevealHelper.RevealStateListener {
+    private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
+
+    private final WallpaperManager mWallpaperManager;
+    private final ImageGLProgram mProgram;
+    private final ImageGLWallpaper mWallpaper;
+    private final ImageProcessHelper mImageProcessHelper;
+    private final ImageRevealHelper mImageRevealHelper;
+    private final ImageGLView mGLView;
+    private boolean mIsInAmbientMode;
+    private float mXOffset = 0f;
+    private float mYOffset = 0f;
+
+    public ImageWallpaperRenderer(Context context, ImageGLView glView) {
+        mWallpaperManager = context.getSystemService(WallpaperManager.class);
+        if (mWallpaperManager == null) {
+            Log.w(TAG, "WallpaperManager not available");
+        }
+
+        mProgram = new ImageGLProgram(context);
+        mWallpaper = new ImageGLWallpaper(mProgram);
+        mImageProcessHelper = new ImageProcessHelper();
+        mImageRevealHelper = new ImageRevealHelper(this);
+        mGLView = glView;
+
+        if (mWallpaperManager != null) {
+            // Compute per85 as transition threshold, this is an async work.
+            mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap());
+        }
+    }
+
+    @Override
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        glClearColor(0f, 0f, 0f, 1.0f);
+        mProgram.useGLProgram(
+                R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
+        mWallpaper.setup();
+        mWallpaper.setupTexture(mWallpaperManager.getBitmap());
+    }
+
+    @Override
+    public void onSurfaceChanged(GL10 gl, int width, int height) {
+        glViewport(0, 0, width, height);
+        mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(),
+                width, height, mXOffset, mYOffset);
+    }
+
+    @Override
+    public void onDrawFrame(GL10 gl) {
+        float threshold = mImageProcessHelper.getPercentile85();
+        float reveal = mImageRevealHelper.getReveal();
+
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), .25f);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
+        glUniform1i(mWallpaper.getHandle(ImageGLWallpaper.U_AOD_MODE), mIsInAmbientMode ? 1 : 0);
+
+        mWallpaper.useTexture();
+        mWallpaper.draw();
+    }
+
+    @Override
+    public void onSensorEvent(boolean awake) {
+        mImageRevealHelper.updateAwake(awake);
+    }
+
+    @Override
+    public void onAmbientModeChanged(boolean inAmbientMode) {
+        mIsInAmbientMode = inAmbientMode;
+        if (inAmbientMode) {
+            mImageRevealHelper.sleep();
+        }
+        requestRender();
+    }
+
+    @Override
+    public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
+        if (frame == null || mWallpaperManager == null
+                || (xOffset == mXOffset && yOffset == mYOffset)) {
+            return;
+        }
+
+        Bitmap bitmap = mWallpaperManager.getBitmap();
+        if (bitmap == null) {
+            return;
+        }
+
+        int width = frame.width();
+        int height = frame.height();
+        mXOffset = xOffset;
+        mYOffset = yOffset;
+
+        mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset);
+        requestRender();
+    }
+
+    @Override
+    public void onRevealStateChanged() {
+        requestRender();
+    }
+
+    private void requestRender() {
+        if (mGLView != null) {
+            mGLView.render();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
old mode 100755
new mode 100644
index ca3cdf4..5f2614e
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -42,6 +42,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -247,7 +248,8 @@
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
-        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter,
+                null, null);
 
         if (sSettingsPackageAndClassNamePairList == null) {
             String[] settings = mContext.getResources().getStringArray(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index ac3ba6b..fa1426e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManager
 import android.content.res.ColorStateList
 import android.os.UserHandle
+import android.provider.Settings
 import android.util.IconDrawableFactory
 import android.view.Gravity
 import android.view.LayoutInflater
@@ -68,7 +69,7 @@
             setPositiveButton(R.string.ongoing_privacy_dialog_ok, null)
             setNeutralButton(R.string.ongoing_privacy_dialog_open_settings,
                     object : DialogInterface.OnClickListener {
-                        val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).putExtra(
+                        val intent = Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS).putExtra(
                                 Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1))
 
                         @Suppress("DEPRECATION")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index a188c5a..0a39549 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -462,13 +462,6 @@
         }
     }
 
-    public void setRestingAlpha(float alpha) {
-        mRestingAlpha = alpha;
-
-        // TODO: Handle the case an animation is playing.
-        setImageAlpha(alpha, false);
-    }
-
     public float getRestingAlpha() {
         return mRestingAlpha;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 158430f..2811505 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -486,7 +486,7 @@
             if (msg.what == MSG_HIDE_TRANSIENT) {
                 hideTransientIndication();
             } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
-                mLockIcon.setTransientFpError(false);
+                mLockIcon.setTransientBiometricsError(false);
             }
         }
     };
@@ -579,7 +579,7 @@
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
                         errorColorState);
             } else if (updateMonitor.isScreenOn()) {
-                mLockIcon.setTransientFpError(true);
+                mLockIcon.setTransientBiometricsError(true);
                 showTransientIndication(helpString, errorColorState);
                 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
                 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
new file mode 100644
index 0000000..752b6a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.Intent;
+import android.service.notification.StatusBarNotification;
+import android.util.FeatureFlagUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+import com.android.settingslib.media.MediaOutputSliceConstants;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Class for handling MediaTransfer state over a set of notifications.
+ */
+public class MediaTransferManager {
+    private final Context mContext;
+    private final ActivityStarter mActivityStarter;
+
+    private final View.OnClickListener mOnClickHandler = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            if (handleMediaTransfer(view)) {
+                return;
+            }
+        }
+
+        private boolean handleMediaTransfer(View view) {
+            if (view.findViewById(com.android.internal.R.id.media_seamless) == null) {
+                return false;
+            }
+
+            ViewParent parent = view.getParent();
+            StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+            final Intent intent = new Intent()
+                    .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+                    .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
+                            statusBarNotification.getPackageName());
+            mActivityStarter.startActivity(intent, false, true /* dismissShade */,
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            return true;
+        }
+
+        private StatusBarNotification getNotificationForParent(ViewParent parent) {
+            while (parent != null) {
+                if (parent instanceof ExpandableNotificationRow) {
+                    return ((ExpandableNotificationRow) parent).getStatusBarNotification();
+                }
+                parent = parent.getParent();
+            }
+            return null;
+        }
+    };
+
+    public MediaTransferManager(Context context) {
+        mContext = context;
+        mActivityStarter = Dependency.get(ActivityStarter.class);
+    }
+
+    /**
+     * apply the action button for MediaTransfer
+     *
+     * @param root  The parent container of the view.
+     * @param entry The entry of MediaTransfer action button.
+     */
+    public void applyMediaTransferView(ViewGroup root, NotificationEntry entry) {
+        if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER)) {
+            return;
+        }
+
+        View view = root.findViewById(com.android.internal.R.id.media_seamless);
+        if (view == null) {
+            return;
+        }
+
+        view.setVisibility(View.VISIBLE);
+        view.setOnClickListener(mOnClickHandler);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 0583843..be63bad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -55,8 +55,6 @@
     private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
     private final Context mContext;
 
-    protected NotificationPresenter mPresenter;
-
     public NotificationListener(Context context) {
         mContext = context;
     }
@@ -152,9 +150,7 @@
         }
     }
 
-    public void setUpWithPresenter(NotificationPresenter presenter) {
-        mPresenter = presenter;
-
+    public void registerAsSystemService() {
         try {
             registerAsSystemService(mContext,
                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f0d804d..3db02b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -54,7 +54,7 @@
     public static NotificationUiAdjustment extractFromNotificationEntry(
             NotificationEntry entry) {
         return new NotificationUiAdjustment(
-                entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
+                entry.key, entry.systemGeneratedSmartActions, entry.systemGeneratedSmartReplies);
     }
 
     public static boolean needReinflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index ef5e936..5c62005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -27,7 +27,6 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -62,13 +61,9 @@
     private static final String TAG = "NotificationEntryMgr";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final Context mContext;
     @VisibleForTesting
     protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
 
-    private final ForegroundServiceController mForegroundServiceController =
-            Dependency.get(ForegroundServiceController.class);
-
     // Lazily retrieved dependencies
     private NotificationRemoteInputManager mRemoteInputManager;
     private NotificationRowBinder mNotificationRowBinder;
@@ -97,7 +92,6 @@
     }
 
     public NotificationEntryManager(Context context) {
-        mContext = context;
         mNotificationData = new NotificationData();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2e93c382..db9fcc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -102,7 +102,9 @@
     /** Smart Actions provided by the NotificationAssistantService. */
     @NonNull
     public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
-    public CharSequence[] smartReplies = new CharSequence[0];
+    /** Smart replies provided by the NotificationAssistantService. */
+    @NonNull
+    public CharSequence[] systemGeneratedSmartReplies = new CharSequence[0];
     @VisibleForTesting
     public int suppressedVisualEffects;
     public boolean suspended;
@@ -182,7 +184,7 @@
         userSentiment = ranking.getUserSentiment();
         systemGeneratedSmartActions = ranking.getSmartActions() == null
                 ? Collections.emptyList() : ranking.getSmartActions();
-        smartReplies = ranking.getSmartReplies() == null
+        systemGeneratedSmartReplies = ranking.getSmartReplies() == null
                 ? new CharSequence[0]
                 : ranking.getSmartReplies().toArray(new CharSequence[0]);
         suppressedVisualEffects = ranking.getSuppressedVisualEffects();
@@ -239,6 +241,13 @@
     }
 
     /**
+     * Returns the data needed for a bubble for this notification, if it exists.
+     */
+    public Notification.BubbleMetadata getBubbleMetadata() {
+        return notification.getNotification().getBubbleMetadata();
+    }
+
+    /**
      * Resets the notification entry to be re-used.
      */
     public void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index c161da3..878d533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -43,6 +43,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.MediaTransferManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
@@ -163,11 +164,13 @@
     private boolean mIsContentExpandable;
     private boolean mRemoteInputVisible;
     private int mUnrestrictedContentHeight;
+    private MediaTransferManager mMediaTransferManager;
 
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext(), this);
+        mMediaTransferManager = new MediaTransferManager(getContext());
         mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
         mSmartReplyController = Dependency.get(SmartReplyController.class);
         initView();
@@ -1250,6 +1253,7 @@
             mAmbientWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
+        applyMediaTransfer(entry);
         updateLegacy();
         mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
@@ -1292,6 +1296,22 @@
         }
     }
 
+    private void applyMediaTransfer(final NotificationEntry entry) {
+        View bigContentView = mExpandedChild;
+        if (bigContentView == null || !entry.isMediaNotification()) {
+            return;
+        }
+
+        View mediaActionContainer = bigContentView.findViewById(
+                com.android.internal.R.id.media_actions);
+        if (!(mediaActionContainer instanceof LinearLayout)) {
+            return;
+        }
+
+        mMediaTransferManager.applyMediaTransferView((ViewGroup) mediaActionContainer,
+                entry);
+    }
+
     private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
         if (mRemoteInputController == null) {
             return;
@@ -1314,16 +1334,21 @@
     static SmartRepliesAndActions chooseSmartRepliesAndActions(
             SmartReplyConstants smartReplyConstants,
             final NotificationEntry entry) {
-        boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
-                && (!smartReplyConstants.requiresTargetingP()
-                || entry.targetSdk >= Build.VERSION_CODES.P));
-
         Notification notification = entry.notification.getNotification();
         Pair<RemoteInput, Notification.Action> remoteInputActionPair =
                 notification.findRemoteInputActionPair(false /* freeform */);
         Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
                 notification.findRemoteInputActionPair(true /* freeform */);
 
+        if (!smartReplyConstants.isEnabled()) {
+            return new SmartRepliesAndActions(null, null, freeformRemoteInputActionPair != null);
+        }
+        // Only use smart replies from the app if they target P or above. We have this check because
+        // the smart reply API has been used for other things (Wearables) in the past. The API to
+        // add smart actions is new in Q so it doesn't require a target-sdk check.
+        boolean enableAppGeneratedSmartReplies = (!smartReplyConstants.requiresTargetingP()
+                || entry.targetSdk >= Build.VERSION_CODES.P);
+
         boolean appGeneratedSmartRepliesExist =
                 enableAppGeneratedSmartReplies
                         && remoteInputActionPair != null
@@ -1348,13 +1373,13 @@
         }
         // Apps didn't provide any smart replies / actions, use those from NAS (if any).
         if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
-            boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+            boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.systemGeneratedSmartReplies)
                     && freeformRemoteInputActionPair != null
                     && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
                     && freeformRemoteInputActionPair.second.actionIntent != null;
             if (useGeneratedReplies) {
                 smartReplies = new SmartReplyView.SmartReplies(
-                        entry.smartReplies,
+                        entry.systemGeneratedSmartReplies,
                         freeformRemoteInputActionPair.first,
                         freeformRemoteInputActionPair.second.actionIntent,
                         true /* fromAssistant */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index cb1384c..aa221993 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -392,12 +391,6 @@
             return false;
         }
 
-        LogMaker logMaker = (row.getStatusBarNotification() == null)
-                ? new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
-                : row.getStatusBarNotification().getLogMaker();
-        mMetricsLogger.write(logMaker.setCategory(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
-                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION));
-
         // ensure that it's laid but not visible until actually laid out
         guts.setVisibility(View.INVISIBLE);
         // Post to ensure the the guts are properly laid out.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 359bc6e..b6948fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -126,18 +126,23 @@
     private OnClickListener mOnKeepShowing = v -> {
         mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
         closeControls(v);
-        mMetricsLogger.write(getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                .setType(MetricsEvent.TYPE_ACTION)
-                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
+        if (mIsForBlockingHelper) {
+            mMetricsLogger.write(getLogMaker().setCategory(
+                    MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setType(MetricsEvent.TYPE_ACTION)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
+        }
     };
 
     private OnClickListener mOnToggleSilent = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
-            mMetricsLogger.write(getLogMaker()
-                    .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                    .setType(MetricsEvent.TYPE_ACTION)
-                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME));
+            if (mIsForBlockingHelper) {
+                mMetricsLogger.write(getLogMaker()
+                        .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME));
+            }
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -149,10 +154,12 @@
     private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_BLOCK, true /* animate */);
-            mMetricsLogger.write(getLogMaker()
-                    .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                    .setType(MetricsEvent.TYPE_ACTION)
-                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED));
+            if (mIsForBlockingHelper) {
+                mMetricsLogger.write(getLogMaker()
+                        .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED));
+            }
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -164,12 +171,16 @@
     private OnClickListener mOnUndo = v -> {
         // Reset exit counter that we'll log and record an undo event separately (not an exit event)
         mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
-        logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
-        mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
+        if (mIsForBlockingHelper) {
+            logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
+            mMetricsLogger.write(getLogMaker().setCategory(
+                    MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setType(MetricsEvent.TYPE_DISMISS)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
+        } else {
+            mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
+        }
         swapContent(ACTION_UNDO, true /* animate */);
-        mMetricsLogger.write(getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                .setType(MetricsEvent.TYPE_DISMISS)
-                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
     };
 
     public NotificationInfo(Context context, AttributeSet attrs) {
@@ -269,11 +280,11 @@
         bindPrompt();
         bindButtons();
 
-        mMetricsLogger.write(getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                .setType(MetricsEvent.TYPE_OPEN)
-                .setSubtype(MetricsEvent.BLOCKING_HELPER_DISPLAY));
+        mMetricsLogger.write(notificationControlsLogMaker());
     }
 
+
+
     private void bindHeader() throws RemoteException {
         // Package name
         Drawable pkgicon = null;
@@ -404,19 +415,6 @@
         }
     }
 
-    /**
-     * Returns an initialized LogMaker for logging importance changes.
-     * The caller may override the type (to DISMISS) before passing it to mMetricsLogger.
-     * @return new LogMaker
-     */
-    private LogMaker importanceChangeLogMaker() {
-        Integer chosenImportance =
-                mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
-        return new LogMaker(MetricsEvent.ACTION_SAVE_IMPORTANCE)
-                .setType(MetricsEvent.TYPE_ACTION)
-                .setSubtype(chosenImportance - mStartingChannelImportance);
-    }
-
     private boolean hasImportanceChanged() {
         return mSingleNotificationChannel != null
                 && mChosenImportance != null
@@ -616,8 +614,8 @@
         confirmation.setAlpha(1f);
         header.setVisibility(VISIBLE);
         header.setAlpha(1f);
-        mMetricsLogger.write(getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
-                .setType(MetricsEvent.TYPE_CLOSE));
+
+        mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
     }
 
     @Override
@@ -764,7 +762,39 @@
         }
     }
 
+    /**
+     * Returns a LogMaker with all available notification information.
+     * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
+     * @return LogMaker
+     */
     private LogMaker getLogMaker() {
-        return mSbn.getLogMaker();
+        // The constructor requires a category, so also do it in the other branch for consistency.
+        return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
+    }
+
+    /**
+     * Returns an initialized LogMaker for logging importance changes.
+     * The caller may override the type before passing it to mMetricsLogger.
+     * @return LogMaker
+     */
+    private LogMaker importanceChangeLogMaker() {
+        Integer chosenImportance =
+                mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
+        return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .setSubtype(chosenImportance - mStartingChannelImportance);
+    }
+
+    /**
+     * Returns an initialized LogMaker for logging open/close of the info display.
+     * The caller may override the type before passing it to mMetricsLogger.
+     * @return LogMaker
+     */
+    private LogMaker notificationControlsLogMaker() {
+        return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
+                .setType(MetricsEvent.TYPE_OPEN)
+                .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
+                        : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 302d630..304d2ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,7 +47,7 @@
     private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
 
     /**
-     * Mode in which we don't need to wake up the device when we get a fingerprint.
+     * Mode in which we don't need to wake up the device when we authenticate.
      */
     public static final int MODE_NONE = 0;
 
@@ -70,8 +70,7 @@
     public static final int MODE_SHOW_BOUNCER = 3;
 
     /**
-     * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
-     * fingerprint.
+     * Mode in which we only wake up the device, and keyguard was not showing when we authenticated.
      * */
     public static final int MODE_ONLY_WAKE = 4;
 
@@ -96,22 +95,21 @@
      */
     private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
 
-    private final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
-    private PowerManager mPowerManager;
-    private Handler mHandler = new Handler();
+    private final NotificationMediaManager mMediaManager;
+    private final PowerManager mPowerManager;
+    private final Handler mHandler;
     private PowerManager.WakeLock mWakeLock;
-    private KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final UnlockMethodCache mUnlockMethodCache;
+    private final StatusBarWindowController mStatusBarWindowController;
+    private final Context mContext;
+    private final int mWakeUpDelay;
     private int mMode;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private StatusBarWindowController mStatusBarWindowController;
     private DozeScrimController mDozeScrimController;
     private KeyguardViewMediator mKeyguardViewMediator;
     private ScrimController mScrimController;
     private StatusBar mStatusBar;
-    private final UnlockMethodCache mUnlockMethodCache;
-    private final Context mContext;
-    private final int mWakeUpDelay;
     private int mPendingAuthenticatedUserId = -1;
     private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
     private boolean mPendingShowBouncer;
@@ -122,11 +120,14 @@
                                      KeyguardViewMediator keyguardViewMediator,
                                      ScrimController scrimController,
                                      StatusBar statusBar,
-                                     UnlockMethodCache unlockMethodCache) {
+                                     UnlockMethodCache unlockMethodCache, Handler handler,
+                                     KeyguardUpdateMonitor keyguardUpdateMonitor,
+                                     int wakeUpDelay) {
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
-        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+        mUpdateMonitor = keyguardUpdateMonitor;
         mUpdateMonitor.registerCallback(this);
+        mMediaManager = Dependency.get(NotificationMediaManager.class);
         Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
         Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
@@ -135,8 +136,8 @@
         mScrimController = scrimController;
         mStatusBar = statusBar;
         mUnlockMethodCache = unlockMethodCache;
-        mWakeUpDelay = context.getResources().getInteger(
-                com.android.internal.R.integer.config_wakeUpDelayDoze);
+        mHandler = handler;
+        mWakeUpDelay = wakeUpDelay;
     }
 
     public void setStatusBarKeyguardViewManager(
@@ -206,7 +207,7 @@
             Trace.endSection();
             return;
         }
-        startWakeAndUnlock(calculateMode());
+        startWakeAndUnlock(calculateMode(biometricSourceType));
     }
 
     public void startWakeAndUnlock(int mode) {
@@ -295,7 +296,7 @@
     }
 
     private void showBouncer() {
-        if (calculateMode() == MODE_SHOW_BOUNCER) {
+        if (mMode == MODE_SHOW_BOUNCER) {
             mStatusBarKeyguardViewManager.showBouncer(false);
         }
         mStatusBarKeyguardViewManager.animateCollapsePanels(
@@ -339,29 +340,30 @@
         return mMode;
     }
 
-    private int calculateMode() {
+    private int calculateMode(BiometricSourceType biometricSourceType) {
         boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
         boolean deviceDreaming = mUpdateMonitor.isDreaming();
+        boolean isFace = biometricSourceType == BiometricSourceType.FACE;
 
         if (!mUpdateMonitor.isDeviceInteractive()) {
             if (!mStatusBarKeyguardViewManager.isShowing()) {
                 return MODE_ONLY_WAKE;
             } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
-                return MODE_WAKE_AND_UNLOCK_PULSING;
+                return isFace ? MODE_NONE : MODE_WAKE_AND_UNLOCK_PULSING;
             } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                 return MODE_WAKE_AND_UNLOCK;
             } else {
                 return MODE_SHOW_BOUNCER;
             }
         }
-        if (unlockingAllowed && deviceDreaming) {
+        if (unlockingAllowed && deviceDreaming && !isFace) {
             return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
         }
         if (mStatusBarKeyguardViewManager.isShowing()) {
             if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
                 return MODE_DISMISS_BOUNCER;
             } else if (unlockingAllowed) {
-                return MODE_UNLOCK;
+                return isFace ? MODE_ONLY_WAKE : MODE_UNLOCK;
             } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 return MODE_SHOW_BOUNCER;
             }
@@ -437,7 +439,7 @@
     }
 
     /**
-     * Successful authentication with fingerprint that wakes up the device.
+     * Successful authentication with fingerprint, face, or iris that wakes up the device.
      */
     public boolean isWakeAndUnlock() {
         return mMode == MODE_WAKE_AND_UNLOCK
@@ -446,7 +448,8 @@
     }
 
     /**
-     * Successful authentication with fingerprint when the screen was either on or off.
+     * Successful authentication with fingerprint, face, or iris when the screen was either
+     * on or off.
      */
     public boolean isBiometricUnlock() {
         return isWakeAndUnlock() || mMode == MODE_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 2fa9415..6fe23fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -251,7 +251,6 @@
         mUnlockMethodCache.addListener(this);
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         mLockIcon.setScreenOn(updateMonitor.isScreenOn());
-        mLockIcon.setDeviceInteractive(updateMonitor.isDeviceInteractive());
         mLockIcon.update();
         setClipChildren(false);
         setClipToPadding(false);
@@ -337,8 +336,8 @@
         updateRightAffordanceIcon();
 
         lp = mLockIcon.getLayoutParams();
-        lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
-        lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
+        lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_lock_width);
+        lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_lock_height);
         mLockIcon.setLayoutParams(lp);
         mLockIcon.setContentDescription(getContext().getText(R.string.accessibility_unlock_button));
         mLockIcon.update(true /* force */);
@@ -748,16 +747,6 @@
                 }
 
                 @Override
-                public void onStartedWakingUp() {
-                    mLockIcon.setDeviceInteractive(true);
-                }
-
-                @Override
-                public void onFinishedGoingToSleep(int why) {
-                    mLockIcon.setDeviceInteractive(false);
-                }
-
-                @Override
                 public void onScreenTurnedOn() {
                     mLockIcon.setScreenOn(true);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index d934d95..60015ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -18,12 +18,13 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.SystemProperties;
 import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.graphics.ColorUtils;
@@ -44,28 +45,33 @@
     private static final int STATE_LOCK_OPEN = 1;
     private static final int STATE_FACE_UNLOCK = 2;
     private static final int STATE_FINGERPRINT = 3;
-    private static final int STATE_FINGERPRINT_ERROR = 4;
+    private static final int STATE_BIOMETRICS_ERROR = 4;
 
     private int mLastState = 0;
-    private boolean mLastDeviceInteractive;
-    private boolean mTransientFpError;
-    private boolean mDeviceInteractive;
+    private boolean mTransientBiometricsError;
     private boolean mScreenOn;
     private boolean mLastScreenOn;
     private Drawable mUserAvatarIcon;
     private final UnlockMethodCache mUnlockMethodCache;
     private AccessibilityController mAccessibilityController;
-    private boolean mHasFingerPrintIcon;
-    private boolean mHasFaceUnlockIcon;
+    private boolean mHasFingerPrintState;
+    private boolean mIsFaceUnlockState;
     private int mDensity;
     private boolean mPulsing;
     private boolean mDozing;
+    private boolean mLastDozing;
+    private boolean mLastPulsing;
 
     private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
     private float mDarkAmount;
 
     public LockIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
+        TypedArray typedArray = context.getTheme().obtainStyledAttributes(
+                attrs, new int[]{ R.attr.backgroundProtectedStyle }, 0, 0);
+        mContext = new ContextThemeWrapper(context,
+                typedArray.getResourceId(0, R.style.BackgroundProtectedStyle));
+        typedArray.recycle();
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
     }
 
@@ -75,13 +81,11 @@
         update();
     }
 
-    public void setTransientFpError(boolean transientFpError) {
-        mTransientFpError = transientFpError;
-        update();
-    }
-
-    public void setDeviceInteractive(boolean deviceInteractive) {
-        mDeviceInteractive = deviceInteractive;
+    /**
+     * If we're currently presenting an authentication error message.
+     */
+    public void setTransientBiometricsError(boolean transientBiometricsError) {
+        mTransientBiometricsError = transientBiometricsError;
         update();
     }
 
@@ -106,21 +110,14 @@
 
     public void update(boolean force) {
         int state = getState();
-        boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
-        mHasFaceUnlockIcon = state == STATE_FACE_UNLOCK;
-        if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
-                || mScreenOn != mLastScreenOn || force) {
-            int iconAnimRes =
-                getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
-                    mDeviceInteractive, mLastScreenOn, mScreenOn);
+        boolean anyFingerprintState = state == STATE_FINGERPRINT
+                || state == STATE_BIOMETRICS_ERROR;
+        mIsFaceUnlockState = state == STATE_FACE_UNLOCK;
+        if (state != mLastState || mLastDozing == mDozing || mLastPulsing == mPulsing
+                || mLastScreenOn != mScreenOn || force) {
+            int iconAnimRes = getAnimationResForTransition(mLastState, state, mLastPulsing,
+                    mPulsing, mLastDozing, mDozing);
             boolean isAnim = iconAnimRes != -1;
-            if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
-                anyFingerprintIcon = true;
-            } else if (iconAnimRes == R.drawable.trusted_state_to_error_animation) {
-                anyFingerprintIcon = true;
-            } else if (iconAnimRes == R.drawable.error_to_trustedstate_animation) {
-                anyFingerprintIcon = true;
-            }
 
             Drawable icon;
             if (isAnim) {
@@ -128,7 +125,7 @@
                 icon = mContext.getDrawable(iconAnimRes);
             } else {
                 // Load the static icon resource based on the current state.
-                icon = getIconForState(state, mScreenOn, mDeviceInteractive);
+                icon = getIconForState(state);
             }
 
             final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
@@ -136,18 +133,18 @@
                     : null;
             setImageDrawable(icon, false);
             updateDarkTint();
-            if (mHasFaceUnlockIcon) {
+            if (mIsFaceUnlockState) {
                 announceForAccessibility(getContext().getString(
                     R.string.accessibility_scanning_face));
             }
 
-            mHasFingerPrintIcon = anyFingerprintIcon;
+            mHasFingerPrintState = anyFingerprintState;
             if (animation != null && isAnim) {
                 animation.forceAnimationOnUI();
                 animation.start();
             }
 
-            if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
+            if (isAnim && !mLastScreenOn) {
                 removeCallbacks(mDrawOffTimeout);
                 postDelayed(mDrawOffTimeout, FP_DRAW_OFF_TIMEOUT);
             } else {
@@ -155,8 +152,9 @@
             }
 
             mLastState = state;
-            mLastDeviceInteractive = mDeviceInteractive;
             mLastScreenOn = mScreenOn;
+            mLastDozing = mDozing;
+            mLastPulsing = mPulsing;
         }
 
         setVisibility(mDozing && !mPulsing ? GONE : VISIBLE);
@@ -167,11 +165,11 @@
         if (mAccessibilityController == null) {
             return;
         }
+        boolean canLock = mUnlockMethodCache.isMethodSecure()
+                && mUnlockMethodCache.canSkipBouncer();
         boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
-        boolean clickToForceLock = mUnlockMethodCache.isTrustManaged()
-                && !clickToUnlock;
-        boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged()
-                && !clickToForceLock;
+        boolean clickToForceLock = canLock && !clickToUnlock;
+        boolean longClickToForceLock = canLock && !clickToForceLock;
         setClickable(clickToForceLock || clickToUnlock);
         setLongClickable(longClickToForceLock);
         setFocusable(mAccessibilityController.isAccessibilityEnabled());
@@ -180,7 +178,7 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        if (mHasFingerPrintIcon) {
+        if (mHasFingerPrintState) {
             AccessibilityNodeInfo.AccessibilityAction unlock
                     = new AccessibilityNodeInfo.AccessibilityAction(
                     AccessibilityNodeInfo.ACTION_CLICK,
@@ -188,7 +186,7 @@
             info.addAction(unlock);
             info.setHintText(getContext().getString(
                     R.string.accessibility_waiting_for_fingerprint));
-        } else if (mHasFaceUnlockIcon){
+        } else if (mIsFaceUnlockState) {
             //Avoid 'button' to be spoken for scanning face
             info.setClassName(LockIcon.class.getName());
             info.setContentDescription(getContext().getString(
@@ -200,26 +198,24 @@
         mAccessibilityController = accessibilityController;
     }
 
-    private Drawable getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
+    private Drawable getIconForState(int state) {
         int iconRes;
         switch (state) {
             case STATE_FINGERPRINT:
             case STATE_LOCKED:
-                iconRes = R.drawable.ic_lock;
+            case STATE_FACE_UNLOCK:
+                iconRes = com.android.internal.R.drawable.ic_lock_24dp;
                 break;
             case STATE_LOCK_OPEN:
                 if (mUnlockMethodCache.isTrustManaged() && mUnlockMethodCache.isTrusted()
                     && mUserAvatarIcon != null) {
                     return mUserAvatarIcon;
                 } else {
-                    iconRes = R.drawable.ic_lock_open;
+                    iconRes = com.android.internal.R.drawable.ic_lock_open_24dp;
                 }
                 break;
-            case STATE_FACE_UNLOCK:
-                iconRes = R.drawable.ic_face_unlock;
-                break;
-            case STATE_FINGERPRINT_ERROR:
-                iconRes = R.drawable.ic_fingerprint_error;
+            case STATE_BIOMETRICS_ERROR:
+                iconRes = com.android.internal.R.drawable.ic_auth_error;
                 break;
             default:
                 throw new IllegalArgumentException();
@@ -229,36 +225,36 @@
     }
 
     private int getAnimationResForTransition(int oldState, int newState,
-            boolean oldDeviceInteractive, boolean deviceInteractive,
-            boolean oldScreenOn, boolean screenOn) {
-        if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
-            return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
-        } else if (oldState == STATE_LOCK_OPEN && newState == STATE_FINGERPRINT_ERROR) {
-            return R.drawable.trusted_state_to_error_animation;
-        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_LOCK_OPEN) {
-            return R.drawable.error_to_trustedstate_animation;
-        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
-            return R.drawable.lockscreen_fingerprint_error_state_to_fp_animation;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
-                && !mUnlockMethodCache.isTrusted()) {
-            return R.drawable.lockscreen_fingerprint_draw_off_animation;
-        } else if (newState == STATE_FINGERPRINT && (!oldScreenOn && screenOn && deviceInteractive
-                || screenOn && !oldDeviceInteractive && deviceInteractive)) {
-            return R.drawable.lockscreen_fingerprint_draw_on_animation;
-        } else {
-            return -1;
+            boolean wasPulsing, boolean pulsing,
+            boolean wasDozing, boolean dozing) {
+
+        boolean isError = newState == STATE_BIOMETRICS_ERROR;
+        boolean isUnlocked = newState == STATE_LOCK_OPEN;
+        boolean isLocked = !isUnlocked;
+        boolean wasUnlocked = oldState == STATE_LOCK_OPEN;
+
+        if (isError) {
+            return com.android.internal.R.anim.lock_to_error;
+        } else if (isUnlocked) {
+            return com.android.internal.R.anim.lock_unlock;
+        } else if (wasUnlocked && isLocked && mScreenOn) {
+            return com.android.internal.R.anim.lock_lock;
+        } else if (isLocked && (!wasPulsing && pulsing || wasDozing && !dozing)) {
+            return com.android.internal.R.anim.lock_in;
         }
+        return -1;
     }
 
     private int getState() {
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
         boolean unlockingAllowed = updateMonitor.isUnlockingWithBiometricAllowed();
-        if (mTransientFpError) {
-            return STATE_FINGERPRINT_ERROR;
+        if (mTransientBiometricsError) {
+            return STATE_BIOMETRICS_ERROR;
         } else if (mUnlockMethodCache.canSkipBouncer()) {
             return STATE_LOCK_OPEN;
-        } else if (mUnlockMethodCache.isFaceUnlockRunning()) {
+        } else if (mUnlockMethodCache.isFaceUnlockRunning()
+                || updateMonitor.isFaceDetectionRunning()) {
             return STATE_FACE_UNLOCK;
         } else if (fingerprintRunning && unlockingAllowed) {
             return STATE_FINGERPRINT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index 39fbbb1..eca14eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -20,12 +20,10 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import static com.android.systemui.statusbar.phone.NavigationPrototypeController.PROTOTYPE_ENABLED;
 
 import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.provider.Settings;
 import android.view.MotionEvent;
 
 import com.android.systemui.recents.OverviewProxyService;
@@ -34,10 +32,6 @@
  * A gesture action that would be triggered and reassigned by {@link QuickStepController}
  */
 public abstract class NavigationGestureAction {
-    private static final String ENABLE_TASK_STABILIZER_FLAG = "ENABLE_TASK_STABILIZER";
-
-    static private boolean sLastTaskStabilizationFlag;
-
     protected final NavigationBarView mNavigationBarView;
     protected final OverviewProxyService mProxySender;
 
@@ -50,9 +44,6 @@
             @NonNull OverviewProxyService service) {
         mNavigationBarView = navigationBarView;
         mProxySender = service;
-        sLastTaskStabilizationFlag = Settings.Global.getInt(
-                mNavigationBarView.getContext().getContentResolver(),
-                ENABLE_TASK_STABILIZER_FLAG, 0) != 0;
     }
 
     /**
@@ -82,15 +73,6 @@
      */
     public void startGesture(MotionEvent event) {
         mIsActive = true;
-
-        // Tell launcher that this action requires a stable task list or not
-        boolean flag = requiresStableTaskList();
-        if (getGlobalBoolean(PROTOTYPE_ENABLED) && flag != sLastTaskStabilizationFlag) {
-            Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
-                    ENABLE_TASK_STABILIZER_FLAG, flag ? 1 : 0);
-            sLastTaskStabilizationFlag = flag;
-        }
-
         onGestureStart(event);
     }
 
@@ -163,13 +145,6 @@
      */
     public abstract boolean isEnabled();
 
-    /**
-     * @return action requires a stable task list from launcher
-     */
-    protected boolean requiresStableTaskList() {
-        return false;
-    }
-
     protected void onDarkIntensityChange(float intensity) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index f4f86eb..b4feb25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,7 +37,6 @@
 
     private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
     public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
-    public static final String PROTOTYPE_ENABLED = "prototype_enabled";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
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 711b08e..a9727c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1800,11 +1800,9 @@
                 || mBarState == StatusBarState.SHADE_LOCKED) {
             boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
             KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
-            if (active && !mUnlockIconActive && mTracking) {
-                lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null);
-            } else if (!active && mUnlockIconActive && mTracking) {
-                lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
-                        150, Interpolators.FAST_OUT_LINEAR_IN, null);
+            if (active != mUnlockIconActive && mTracking) {
+                lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true, 150,
+                        Interpolators.FAST_OUT_LINEAR_IN, null);
             }
             mUnlockIconActive = active;
         }
@@ -2015,7 +2013,6 @@
                 || mBarState == StatusBarState.SHADE_LOCKED)) {
             KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
             lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null);
-            lockIcon.setImageScale(2.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
index 1999f9a..b18b79e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
@@ -47,10 +47,6 @@
         return mNavigationBarView.isQuickStepSwipeUpEnabled();
     }
 
-    protected boolean requiresStableTaskList() {
-        return true;
-    }
-
     @Override
     public void onGestureStart(MotionEvent event) {
         try {
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 ceadd1e..c129143 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -354,6 +354,8 @@
     protected StatusBarWindowController mStatusBarWindowController;
     protected UnlockMethodCache mUnlockMethodCache;
     @VisibleForTesting
+    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @VisibleForTesting
     DozeServiceHost mDozeServiceHost = new DozeServiceHost();
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
@@ -480,8 +482,7 @@
             updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
             // If WallpaperInfo is null, it must be ImageWallpaper.
             final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && (info == null && aodImageWallpaperEnabled
-                        || info != null && info.supportsAmbientMode());
+                    && (info == null || info.supportsAmbientMode());
 
             mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -613,6 +614,7 @@
         mNotificationLogger = Dependency.get(NotificationLogger.class);
         mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
         mNotificationListener =  Dependency.get(NotificationListener.class);
+        mNotificationListener.registerAsSystemService();
         mNetworkController = Dependency.get(NetworkController.class);
         mUserSwitcherController = Dependency.get(UserSwitcherController.class);
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
@@ -674,7 +676,7 @@
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
 
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
@@ -761,7 +763,7 @@
         mUnlockMethodCache.addListener(this);
         startKeyguard();
 
-        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
+        mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
         putComponent(DozeHost.class, mDozeServiceHost);
 
         mScreenPinningRequest = new ScreenPinningRequest(mContext);
@@ -1055,7 +1057,6 @@
                         mDeviceProvisionedController);
 
         mAppOpsController.addCallback(APP_OPS, this);
-        mNotificationListener.setUpWithPresenter(mPresenter);
         mNotificationShelf.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
 
@@ -1207,7 +1208,9 @@
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mBiometricUnlockController = new BiometricUnlockController(mContext,
                 mDozeScrimController, keyguardViewMediator,
-                mScrimController, this, UnlockMethodCache.getInstance(mContext));
+                mScrimController, this, UnlockMethodCache.getInstance(mContext),
+                new Handler(), mKeyguardUpdateMonitor, mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_wakeUpDelayDoze));
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                 getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
         mKeyguardIndicationController
@@ -1445,6 +1448,7 @@
         return new StatusBar.H();
     }
 
+    @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags) {
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, flags);
@@ -2357,8 +2361,8 @@
             mLightBarController.dump(fd, pw, args);
         }
 
-        if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
-            KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
+        if (mKeyguardUpdateMonitor != null) {
+            mKeyguardUpdateMonitor.dump(fd, pw, args);
         }
 
         FalsingManager.getInstance(mContext).dump(pw);
@@ -3891,6 +3895,11 @@
                 return;
             }
 
+            if (mKeyguardUpdateMonitor != null
+                    && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+                mKeyguardUpdateMonitor.onAuthInterruptDetected();
+            }
+
             // Set the state to pulsing, so ScrimController will know what to do once we ask it to
             // execute the transition. The pulse callback will then be invoked when the scrims
             // are black, indicating that StatusBar is ready to present the rest of the UI.
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
index 52cabe2..dd1d0ca 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -156,6 +156,8 @@
 
     private boolean checkIfNeedMask() {
         // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
+        // Because of conflicting with another wallpaper feature,
+        // we only support LockScreen wallpaper currently.
         return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2055519..d5a275e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,15 +18,33 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.common.truth.Truth.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
+import android.os.UserManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -39,6 +57,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -52,37 +72,71 @@
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardUpdateMonitorTest extends SysuiTestCase {
 
+    @Mock
+    private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker;
+    @Mock
+    private TrustManager mTrustManager;
+    @Mock
+    private FingerprintManager mFingerprintManager;
+    @Mock
+    private FaceManager mFaceManager;
+    @Mock
+    private BiometricManager mBiometricManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private DevicePolicyManager mDevicePolicyManager;
     private TestableLooper mTestableLooper;
+    private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
+        TestableContext context = spy(mContext);
+        when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
+        when(context.getPackageManager()).thenReturn(mPackageManager);
+        doAnswer(invocation -> {
+            IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
+            callback.onChanged(BiometricSourceType.FACE, true /* enabled */);
+            return null;
+        }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        context.addMockSystemService(TrustManager.class, mTrustManager);
+        context.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+        context.addMockSystemService(BiometricManager.class, mBiometricManager);
+        context.addMockSystemService(FaceManager.class, mFaceManager);
+        context.addMockSystemService(UserManager.class, mUserManager);
+        context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+
         mTestableLooper = TestableLooper.get(this);
+        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
     }
 
     @Test
     public void testIgnoresSimStateCallback_rebroadcast() {
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
 
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
-
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
         mTestableLooper.processAllMessages();
         Assert.assertTrue("onSimStateChanged not called",
-                keyguardUpdateMonitor.hasSimStateJustChanged());
+                mKeyguardUpdateMonitor.hasSimStateJustChanged());
 
         intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
         mTestableLooper.processAllMessages();
         Assert.assertFalse("onSimStateChanged should have been skipped",
-                keyguardUpdateMonitor.hasSimStateJustChanged());
+                mKeyguardUpdateMonitor.hasSimStateJustChanged());
     }
 
     @Test
     public void testTelephonyCapable_BootInitState() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
@@ -90,36 +144,30 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent,null, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
     }
 
     @Test
     public void testTelephonyCapable_SimInvalid_ServiceState_InService() {
         // SERVICE_STATE - IN_SERVICE, but SIM_STATE is invalid TelephonyCapable should be False
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
         state.fillInNotifierBundle(data);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
     public void testTelephonyCapable_SimValid_ServiceState_PowerOff() {
         // Simulate AirplaneMode case, SERVICE_STATE - POWER_OFF, check TelephonyCapable False
         // Only receive ServiceState callback IN_SERVICE -> OUT_OF_SERVICE -> POWER_OFF
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_LOADED);
@@ -127,10 +175,10 @@
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_POWER_OFF);
         state.fillInNotifierBundle(data);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, true));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
     }
 
     /* Normal SIM inserted flow
@@ -142,24 +190,20 @@
      */
     @Test
     public void testTelephonyCapable_BootInitState_ServiceState_OutOfService() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
         state.fillInNotifierBundle(data);
         intent.putExtras(data);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
     public void testTelephonyCapable_BootInitState_SimState_NotReady() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -167,16 +211,14 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_NOT_READY);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
     public void testTelephonyCapable_BootInitState_SimState_Ready() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -184,46 +226,40 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_READY);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
     public void testTelephonyCapable_BootInitState_ServiceState_PowerOff() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_POWER_OFF);
         state.fillInNotifierBundle(data);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, false));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
     }
 
     @Test
     public void testTelephonyCapable_SimValid_ServiceState_InService() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
         state.fillInNotifierBundle(data);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intent, data, true));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
     }
 
     @Test
     public void testTelephonyCapable_SimValid_SimState_Loaded() {
-        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
-                new TestableKeyguardUpdateMonitor(getContext());
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
@@ -231,19 +267,96 @@
         Intent intentSimState = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intentSimState, data, true));
         mTestableLooper.processAllMessages();
         // Even SimState Loaded, still need ACTION_SERVICE_STATE_CHANGED turn on mTelephonyCapable
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
 
         Intent intentServiceState =  new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
                 , IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intentServiceState, data, true));
         mTestableLooper.processAllMessages();
-        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    @Test
+    public void testTriesToAuthenticate_whenBouncer() {
+        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+        mTestableLooper.processAllMessages();
+
+        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+        verify(mFaceManager).isHardwareDetected();
+        verify(mFaceManager).hasEnrolledTemplates(anyInt());
+    }
+
+    @Test
+    public void testTriesToAuthenticate_whenKeyguard() {
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void skipsAuthentication_whenEncryptedKeyguard() {
+        reset(mUserManager);
+        when(mUserManager.isUserUnlocked(anyInt())).thenReturn(false);
+
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testTriesToAuthenticate_whenAssistant() {
+        mKeyguardUpdateMonitor.setKeyguardOccluded(true);
+        mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testNeverAuthenticates_whenFaceLockout() {
+        mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+                .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT, "lockout");
+        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+        mTestableLooper.processAllMessages();
+
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
+        mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
+        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+        mTestableLooper.processAllMessages();
+
+        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testGetUserCanSkipBouncer_whenFace() {
+        int user = KeyguardUpdateMonitor.getCurrentUser();
+        mKeyguardUpdateMonitor.onFaceAuthenticated(user);
+        assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
+    }
+
+    @Test
+    public void testGetUserCanSkipBouncer_whenFingerprint() {
+        int user = KeyguardUpdateMonitor.getCurrentUser();
+        mKeyguardUpdateMonitor.onFingerprintAuthenticated(user);
+        assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
+    }
+
+    @Test
+    public void testGetUserCanSkipBouncer_whenTrust() {
+        int user = KeyguardUpdateMonitor.getCurrentUser();
+        mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
+        assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
@@ -261,8 +374,9 @@
 
         protected TestableKeyguardUpdateMonitor(Context context) {
             super(context);
-            // Avoid race condition when unexpected broadcast could be received.
             context.unregisterReceiver(mBroadcastReceiver);
+            context.unregisterReceiver(mBroadcastAllReceiver);
+            mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker;
         }
 
         public boolean hasSimStateJustChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6a3bd73..e32d48d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -77,6 +77,10 @@
 
     @Mock
     private NotificationData mNotificationData;
+    @Mock
+    private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
+    @Mock
+    private BubbleController.BubbleExpandListener mBubbleExpandListener;
 
     @Before
     public void setUp() throws Exception {
@@ -101,6 +105,8 @@
         when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
 
         mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+        mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
+        mBubbleController.setExpandListener(mBubbleExpandListener);
 
         // Get a reference to the BubbleController's entry listener
         verify(mNotificationEntryManager, atLeastOnce())
@@ -112,6 +118,8 @@
     public void testAddBubble() {
         mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
+
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
     }
 
     @Test
@@ -126,10 +134,14 @@
         mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
 
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
         mBubbleController.removeBubble(mRow.getEntry().key);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
         assertTrue(mRow.getEntry().isBubbleDismissed());
         verify(mNotificationEntryManager).updateNotifications();
+
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
     }
 
     @Test
@@ -141,41 +153,133 @@
         mBubbleController.dismissStack();
         assertFalse(mStatusBarWindowController.getBubblesShowing());
         verify(mNotificationEntryManager).updateNotifications();
+        assertTrue(mRow.getEntry().isBubbleDismissed());
+        assertTrue(mRow2.getEntry().isBubbleDismissed());
     }
 
     @Test
-    public void testIsStackExpanded() {
+    public void testExpandCollapseStack() {
         assertFalse(mBubbleController.isStackExpanded());
+
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
 
+        // We should have bubbles & their notifs should show in the shade
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        // Expand the stack
         BubbleStackView stackView = mBubbleController.getStackView();
         stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
 
+        // Make sure it's no longer in the shade
+        assertFalse(mRow.getEntry().showInShadeWhenBubble());
+
+        // Collapse
         stackView.collapseStack();
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
         assertFalse(mBubbleController.isStackExpanded());
     }
 
     @Test
-    public void testCollapseStack() {
+    public void testCollapseAfterChangingExpandedBubble() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
 
+        // We should have bubbles & their notifs should show in the shade
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+        assertTrue(mRow2.getEntry().showInShadeWhenBubble());
+
+        // Expand
         BubbleStackView stackView = mBubbleController.getStackView();
         stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
 
+        // Last added is the one that is expanded
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertFalse(mRow2.getEntry().showInShadeWhenBubble());
+
+        // Switch which bubble is expanded
         stackView.setExpandedBubble(mRow.getEntry());
-        assertEquals(stackView.getExpandedBubble().getEntry(), mRow.getEntry());
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertFalse(mRow.getEntry().showInShadeWhenBubble());
 
-        stackView.setExpandedBubble(mRow2.getEntry());
-        assertEquals(stackView.getExpandedBubble().getEntry(), mRow2.getEntry());
+        // collapse for previous bubble
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+        // expand for selected bubble
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
 
+        // Collapse
         mBubbleController.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
     }
 
     @Test
+    public void testExpansionRemovesShowInShade() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+
+        // We should have bubbles & their notifs should show in the shade
+        assertTrue(mBubbleController.hasBubbles());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+        // Expand
+        BubbleStackView stackView = mBubbleController.getStackView();
+        stackView.expandStack();
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+
+        // No longer show shade in notif after expansion
+        assertFalse(mRow.getEntry().showInShadeWhenBubble());
+    }
+
+    @Test
+    public void testRemoveLastExpandedCollapses() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+        mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
+        // Expand
+        BubbleStackView stackView = mBubbleController.getStackView();
+        stackView.expandStack();
+
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
+
+        // Last added is the one that is expanded
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertFalse(mRow2.getEntry().showInShadeWhenBubble());
+
+        // Dismiss currently expanded
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+
+        // Make sure next bubble is selected
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+
+        // Dismiss that one
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+
+        // Make sure state changes and collapse happens
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
     public void testMarkNewNotificationAsBubble() {
         mEntryListener.onPendingEntryAdded(mRow.getEntry());
         assertTrue(mRow.getEntry().isBubble());
@@ -200,7 +304,7 @@
         }
 
         @Override
-        public boolean shouldAutoBubble(Context c, NotificationEntry entry) {
+        public boolean shouldAutoBubbleForFlags(Context c, NotificationEntry entry) {
             return entry.notification.getNotification().getBubbleMetadata() != null;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
index bfc02d9..5be991f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
@@ -75,7 +75,7 @@
     }
 
     @Test
-    public void testRenderVisibility() {
+    public void testRenderVisibility() throws InterruptedException {
         mLayout.setController(mTestableController);
         addOneMoreThanRenderLimitBubbles();
 
@@ -87,7 +87,7 @@
     }
 
     @Test
-    public void testHierarchyChanges() {
+    public void testHierarchyChanges() throws InterruptedException {
         mLayout.setController(mTestableController);
         addOneMoreThanRenderLimitBubbles();
 
@@ -242,26 +242,6 @@
     }
 
     @Test
-    public void testPrecedingNonRemovedIndex() {
-        mLayout.setController(mTestableController);
-        addOneMoreThanRenderLimitBubbles();
-
-        // Call removeView at index 4, but don't actually remove it yet (as if we're animating it
-        // out). The preceding, non-removed view index to 3 should initially be 4, but then 5 since
-        // 4 is on its way out.
-        assertEquals(4, mLayout.getPrecedingNonRemovedViewIndex(3));
-        mLayout.removeView(mViews.get(4));
-        assertEquals(5, mLayout.getPrecedingNonRemovedViewIndex(3));
-
-        // Call removeView at index 1, and actually remove it immediately. With the old view at 1
-        // instantly gone, the preceding view to 0 should be 1 in both cases.
-        assertEquals(1, mLayout.getPrecedingNonRemovedViewIndex(0));
-        mTestableController.setRemoveImmediately(true);
-        mLayout.removeView(mViews.get(1));
-        assertEquals(1, mLayout.getPrecedingNonRemovedViewIndex(0));
-    }
-
-    @Test
     public void testSetController() throws InterruptedException {
         // Add the bubbles, then set the controller, to make sure that a controller added to an
         // already-initialized view works correctly.
@@ -360,8 +340,6 @@
 
         mLayout.cancelAllAnimations();
 
-        waitForLayoutMessageQueue();
-
         // Animations should be somewhere before their end point.
         assertTrue(mViews.get(0).getTranslationX() < 1000);
         assertTrue(mViews.get(0).getTranslationY() < 1000);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 186a762..31e44d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -23,6 +23,7 @@
 import android.os.Looper;
 import android.view.DisplayCutout;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
@@ -93,7 +94,7 @@
     }
 
     /** Add one extra bubble over the limit, so we can make sure it's gone/chains appropriately. */
-    void addOneMoreThanRenderLimitBubbles() {
+    void addOneMoreThanRenderLimitBubbles() throws InterruptedException {
         for (int i = 0; i < mMaxRenderedBubbles + 1; i++) {
             final View newView = new FrameLayout(mContext);
             mLayout.addView(newView, 0);
@@ -129,7 +130,7 @@
     void waitForLayoutMessageQueue() throws InterruptedException {
         // Wait for layout, then the view should be actually removed.
         CountDownLatch layoutLatch = new CountDownLatch(1);
-        mLayout.post(layoutLatch::countDown);
+        mMainThreadHandler.post(layoutLatch::countDown);
         layoutLatch.await(1, TimeUnit.SECONDS);
     }
 
@@ -145,11 +146,7 @@
         @Override
         public void setController(PhysicsAnimationController controller) {
             mMainThreadHandler.post(() -> super.setController(controller));
-            try {
-                waitForLayoutMessageQueue();
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
+            waitForMessageQueueAndIgnoreIfInterrupted();
         }
 
         @Override
@@ -169,6 +166,32 @@
             return mWindowInsets;
         }
 
+        @Override
+        public void removeView(View view) {
+            mMainThreadHandler.post(() ->
+                    super.removeView(view));
+            waitForMessageQueueAndIgnoreIfInterrupted();
+        }
+
+        @Override
+        public void addView(View child, int index, ViewGroup.LayoutParams params) {
+            mMainThreadHandler.post(() ->
+                    super.addView(child, index, params));
+            waitForMessageQueueAndIgnoreIfInterrupted();
+        }
+
+        /**
+         * Wait for the queue but just catch and print the exception if interrupted, since we can't
+         * just add the exception to the overridden methods' signatures.
+         */
+        private void waitForMessageQueueAndIgnoreIfInterrupted() {
+            try {
+                waitForLayoutMessageQueue();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
         /**
          * Sets an end listener that will be called after the 'real' end listener that was already
          * set.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index db819d5..97935ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -178,17 +178,20 @@
 
     @Test
     public void testChildRemoved() throws InterruptedException {
+        assertEquals(0, mLayout.getTransientViewCount());
+
         final View firstView = mLayout.getChildAt(0);
         mLayout.removeView(firstView);
 
-        // The view should still be there, since the controller is animating it out and hasn't yet
-        // actually removed it from the parent view.
-        assertEquals(0, mLayout.indexOfChild(firstView));
+        // The view should now be transient, and missing from the view's normal hierarchy.
+        assertEquals(1, mLayout.getTransientViewCount());
+        assertEquals(-1, mLayout.indexOfChild(firstView));
 
         waitForPropertyAnimations(DynamicAnimation.ALPHA);
         waitForLayoutMessageQueue();
 
-        assertEquals(-1, mLayout.indexOfChild(firstView));
+        // The view should now be gone entirely, no transient views left.
+        assertEquals(0, mLayout.getTransientViewCount());
 
         // The subsequent view should have been translated over to 0, not stacked off to the left.
         assertEquals(0, mLayout.getChildAt(0).getTranslationX(), .1f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index e1c481e..5a6200f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -230,9 +230,9 @@
     public void testPulseReason_getMatchesRequest() {
         mMachine.requestState(INITIALIZED);
         mMachine.requestState(DOZE);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP);
+        mMachine.requestPulse(DozeLog.REASON_SENSOR_DOUBLE_TAP);
 
-        assertEquals(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
+        assertEquals(DozeLog.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 7b358b9..cdac7c97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -125,7 +125,7 @@
     public void testOnSensor_whenUndockedWithNearAndDoubleTapScreen_shouldNotWakeUp() {
         mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
 
-        mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+        mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
                 false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
                 null /* rawValues */);
 
@@ -137,7 +137,7 @@
         doReturn(true).when(mDockManagerFake).isDocked();
         mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
 
-        mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+        mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
                 false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
                 null /* rawValues */);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 425ca58..8e926848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -97,7 +97,6 @@
                 mDelegate);
         lockscreenUserManager.setUpWithPresenter(mPresenter);
         viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
-        notificationListener.setUpWithPresenter(mPresenter);
 
         TestableLooper.get(this).processAllMessages();
         assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowController.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index bcf5964..cffa9c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -77,8 +77,6 @@
         mListener = new NotificationListener(mContext);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.CURRENT, null, 0);
-
-        mListener.setUpWithPresenter(mPresenter);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index d94bf84..c80396d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -193,14 +193,33 @@
     }
 
     @Test
-    public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartReplies() {
-        setupAppGeneratedReplies(new String[] {"Reply1", "Reply2"});
+    public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() {
+        CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+        List<Notification.Action> smartActions =
+                createActions(new String[] {"Test Action 1", "Test Action 2"});
+        setupAppGeneratedSuggestions(smartReplies, smartActions);
         when(mSmartReplyConstants.isEnabled()).thenReturn(false);
 
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
         assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions).isNull();
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() {
+        mEntry.systemGeneratedSmartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        when(mSmartReplyConstants.isEnabled()).thenReturn(false);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -238,11 +257,13 @@
         // replies.
         setupAppGeneratedReplies(null /* smartReplies */);
 
-        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
+                mEntry.systemGeneratedSmartReplies);
         assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
         assertThat(repliesAndActions.smartActions).isNull();
     }
@@ -253,7 +274,7 @@
         // replies.
         setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
 
-        mEntry.smartReplies =
+        mEntry.systemGeneratedSmartReplies =
                 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -288,7 +309,8 @@
                 createActions(new String[] {"Test Action 1", "Test Action 2"});
         setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
 
-        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
 
@@ -307,7 +329,8 @@
         // actions.
         setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
-        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index fdc9e0c..727e9af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -45,7 +45,6 @@
 import android.app.NotificationChannel;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Handler;
 import android.provider.Settings;
@@ -57,7 +56,6 @@
 import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -220,34 +218,6 @@
     }
 
     @Test
-    public void testOpenGutsLogging() {
-        NotificationGutsManager gutsManager = spy(mGutsManager);
-        doReturn(true).when(gutsManager).bindGuts(any(), any());
-
-        NotificationGuts guts = spy(new NotificationGuts(mContext));
-        doReturn(true).when(guts).post(any());
-
-        ExpandableNotificationRow realRow = createTestNotificationRow();
-        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
-        ExpandableNotificationRow row = spy(realRow);
-        when(row.getWindowToken()).thenReturn(new Binder());
-        when(row.getGuts()).thenReturn(guts);
-        StatusBarNotification notification = spy(realRow.getStatusBarNotification());
-        when(row.getStatusBarNotification()).thenReturn(notification);
-
-        assertTrue(gutsManager.openGuts(row, 0, 0, menuItem));
-
-        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
-        verify(notification).getLogMaker();
-        verify(mMetricsLogger).write(logMakerCaptor.capture());
-        assertEquals(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS,
-                logMakerCaptor.getValue().getCategory());
-        assertEquals(MetricsProto.MetricsEvent.TYPE_ACTION,
-                logMakerCaptor.getValue().getType());
-    }
-
-    @Test
     public void testAppOpsSettingsIntent_camera() {
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(OP_CAMERA);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 554baaf..2a64445 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -83,7 +83,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -107,11 +106,16 @@
     private NotificationChannel mDefaultNotificationChannel;
     private StatusBarNotification mSbn;
 
-    @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private MetricsLogger mMetricsLogger;
-    @Mock private INotificationManager mMockINotificationManager;
-    @Mock private PackageManager mMockPackageManager;
-    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    @Mock
+    private INotificationManager mMockINotificationManager;
+    @Mock
+    private PackageManager mMockPackageManager;
+    @Mock
+    private NotificationBlockingHelperManager mBlockingHelperManager;
 
     @Before
     public void setUp() throws Exception {
@@ -172,44 +176,25 @@
         PollingCheck.waitFor(1000,
                 () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
     }
+
     private void ensureNoUndoButton() {
         PollingCheck.waitFor(1000,
                 () -> GONE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()
                         && !mNotificationInfo.isAnimating());
     }
+
     private void waitForStopButton() {
         PollingCheck.waitFor(1000,
                 () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
     }
 
-    class ImportanceChangeLogMaker implements ArgumentMatcher<LogMaker> {
-        private static final int CATEGORY = MetricsProto.MetricsEvent.ACTION_SAVE_IMPORTANCE;
-        private int mType, mSubtype;
-
-        ImportanceChangeLogMaker(int type, int subtype) {
-            mType = type;
-            mSubtype = subtype;
-        }
-        public boolean matches(LogMaker l) {
-            return (l.getCategory() == CATEGORY)
-                    && (l.getType() == mType)
-                    && (l.getSubtype() == mSubtype);
-        }
-
-        public String toString() {
-            return String.format("LogMaker(%d, %d, %d)", CATEGORY, mType, mSubtype);
-        }
-    }
-
-    private LogMaker importanceChangeLog(int type, int subtype) {
-        return argThat(new ImportanceChangeLogMaker(type, subtype));
-    }
-
     @Test
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                true, false,
                 IMPORTANCE_DEFAULT, true);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
@@ -330,9 +315,9 @@
 
     @Test
     public void testBindNotification_BlockButton() throws Exception {
-       mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-               TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-               IMPORTANCE_DEFAULT, true);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT, true);
         final View block = mNotificationInfo.findViewById(R.id.int_block);
         final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
         assertEquals(VISIBLE, block.getVisibility());
@@ -340,7 +325,7 @@
     }
 
     @Test
-    public void testBindNotification_BlockButton_BlockHelper() throws Exception {
+    public void testBindNotification_BlockButton_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
                 true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT, true);
@@ -498,13 +483,29 @@
     }
 
     @Test
-    public void testLogBlockingHelperCounter_logGutsViewDisplayed() throws Exception {
+    public void testBindNotificationLogging_notBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                true, false,
                 IMPORTANCE_DEFAULT, true);
-        mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger).write(argThat(logMaker ->
-                logMaker.getCategory() == MetricsEvent.NOTIFICATION_BLOCKING_HELPER
+                logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
+                        && logMaker.getType() == MetricsEvent.TYPE_OPEN
+                        && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_UNKNOWN
+        ));
+    }
+
+    @Test
+    public void testBindNotificationLogging_BlockingHelper() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                false, true,
+                true, true,
+                IMPORTANCE_DEFAULT, true);
+        verify(mMetricsLogger).write(argThat(logMaker ->
+                logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
                         && logMaker.getType() == MetricsEvent.TYPE_OPEN
                         && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_DISPLAY
         ));
@@ -513,8 +514,11 @@
     @Test
     public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true, true, IMPORTANCE_DEFAULT, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                false, true,
+                true, true,
+                IMPORTANCE_DEFAULT, true);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
     }
@@ -680,7 +684,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true, false /* isNonblockable */, IMPORTANCE_DEFAULT, false
         );
 
@@ -722,7 +726,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
                 true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
@@ -744,14 +748,14 @@
     }
 
     @Test
-    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing()
+    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing_BlockingHelper()
             throws Exception {
         NotificationInfo.CheckSaveListener listener =
                 mock(NotificationInfo.CheckSaveListener.class);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
                 true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
 
@@ -772,14 +776,14 @@
     }
 
     @Test
-    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss()
+    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss_BlockingHelper()
             throws Exception {
         NotificationInfo.CheckSaveListener listener =
                 mock(NotificationInfo.CheckSaveListener.class);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                null /* onSettingsClick */, null /* onAppSettingsClick */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
                 true, true /* isUserSentimentNegative */,  /* isNoisy */
                 IMPORTANCE_DEFAULT, true);
@@ -791,7 +795,7 @@
     }
 
     @Test
-    public void testCloseControls_checkSaveListenerDelaysStopNotifications()
+    public void testCloseControls_checkSaveListenerDelaysStopNotifications_BlockingHelper()
             throws Exception {
         NotificationInfo.CheckSaveListener listener =
                 mock(NotificationInfo.CheckSaveListener.class);
@@ -849,18 +853,25 @@
     }
 
     @Test
-    public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
+    public void testBlockChangedCallsUpdateNotificationChannel_notBlockingHelper()
+            throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                true, false,
                 IMPORTANCE_DEFAULT, false);
 
         mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
-        verify(mMetricsLogger).write(importanceChangeLog(
-                MetricsProto.MetricsEvent.TYPE_ACTION, IMPORTANCE_NONE - IMPORTANCE_LOW));
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        verify(mMetricsLogger, times(2)).write(logMakerCaptor.capture());
+        assertEquals(MetricsProto.MetricsEvent.TYPE_ACTION,
+                logMakerCaptor.getValue().getType());
+        assertEquals(IMPORTANCE_NONE - IMPORTANCE_LOW,
+                logMakerCaptor.getValue().getSubtype());
 
         mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
@@ -896,8 +907,12 @@
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
-        verify(mMetricsLogger).write(importanceChangeLog(
-                MetricsProto.MetricsEvent.TYPE_ACTION, IMPORTANCE_NONE - IMPORTANCE_LOW));
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        verify(mMetricsLogger, times(3)).write(logMakerCaptor.capture());
+        assertEquals(MetricsProto.MetricsEvent.TYPE_ACTION,
+                logMakerCaptor.getValue().getType());
+        assertEquals(IMPORTANCE_NONE - IMPORTANCE_LOW,
+                logMakerCaptor.getValue().getSubtype());
 
         mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
@@ -965,10 +980,12 @@
     }
 
     @Test
-    public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception {
+    public void testBlockUndoDoesNotBlockNotificationChannel_notBlockingHelper() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null,
+                true, false,
                 IMPORTANCE_DEFAULT, false);
 
         mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -977,8 +994,15 @@
         waitForStopButton();
         // mNotificationInfo.handleCloseControls doesn't get called by this interaction.
 
-        verify(mMetricsLogger).write(importanceChangeLog(
-                MetricsProto.MetricsEvent.TYPE_DISMISS, IMPORTANCE_NONE - IMPORTANCE_LOW));
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        verify(mMetricsLogger, times(2)).write(logMakerCaptor.capture());
+        assertEquals(MetricsEvent.ACTION_SAVE_IMPORTANCE,
+                logMakerCaptor.getValue().getCategory());
+        assertEquals(MetricsEvent.TYPE_DISMISS,
+                logMakerCaptor.getValue().getType());
+        assertEquals(IMPORTANCE_NONE - IMPORTANCE_LOW,
+                logMakerCaptor.getValue().getSubtype());
+
 
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -986,11 +1010,12 @@
     }
 
     @Test
-    public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
+    public void testMinUndoDoesNotMinNotificationChannel_notBlockingHelper() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                IMPORTANCE_DEFAULT, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                null, null, null, true,
+                true, IMPORTANCE_DEFAULT, false);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
new file mode 100644
index 0000000..7be4756
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+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;
+
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.statusbar.NotificationMediaManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class BiometricsUnlockControllerTest extends SysuiTestCase {
+
+    @Mock
+    private NotificationMediaManager mMediaManager;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    @Mock
+    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    @Mock
+    private StatusBarWindowController mStatusBarWindowController;
+    @Mock
+    private DozeScrimController mDozeScrimController;
+    @Mock
+    private KeyguardViewMediator mKeyguardViewMediator;
+    @Mock
+    private ScrimController mScrimController;
+    @Mock
+    private StatusBar mStatusBar;
+    @Mock
+    private UnlockMethodCache mUnlockMethodCache;
+    private BiometricUnlockController mBiometricUnlockController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+        mContext.addMockSystemService(PowerManager.class, mPowerManager);
+        mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
+        mDependency.injectTestDependency(StatusBarWindowController.class,
+                mStatusBarWindowController);
+        mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
+                mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache,
+                new Handler(), mUpdateMonitor, 0 /* wakeUpDelay */);
+        mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FINGERPRINT);
+        verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+        verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFingerprintAndNotInteractive_wakeAndUnlock() {
+        reset(mUpdateMonitor);
+        reset(mStatusBarKeyguardViewManager);
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        when(mDozeScrimController.isPulsing()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FINGERPRINT);
+
+        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() {
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FINGERPRINT);
+
+        verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+        verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FINGERPRINT);
+
+        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFace_dontDismissKeyguard() {
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FACE);
+
+        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FACE);
+
+        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+    }
+
+    @Test
+    public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() {
+        reset(mUpdateMonitor);
+        reset(mStatusBarKeyguardViewManager);
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+        when(mDozeScrimController.isPulsing()).thenReturn(true);
+        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+                BiometricSourceType.FACE);
+
+        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+    }
+}
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 17611ff..49fcafd 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
@@ -64,6 +64,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.InitController;
@@ -120,6 +121,9 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -161,6 +165,8 @@
     private NotificationAlertingManager mNotificationAlertingManager;
     @Mock
     private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
@@ -252,7 +258,7 @@
                 mDozeScrimController, mock(NotificationShelf.class),
                 mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
                 mock(BubbleController.class), mock(NavigationBarController.class),
-                mock(AutoHideController.class));
+                mock(AutoHideController.class), mKeyguardUpdateMonitor);
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         SystemUIFactory.getInstance().getRootComponent()
@@ -631,6 +637,39 @@
     }
 
     @Test
+    public void testPulseWhileDozing_notifyAuthInterrupt() {
+        HashSet<Integer> reasonsWantingAuth = new HashSet<>(
+                Collections.singletonList(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
+        HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
+                Arrays.asList(DozeLog.PULSE_REASON_INTENT,
+                        DozeLog.PULSE_REASON_NOTIFICATION,
+                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
+                        DozeLog.REASON_SENSOR_PICKUP,
+                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
+                        DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
+                        DozeLog.PULSE_REASON_DOCKING,
+                        DozeLog.REASON_SENSOR_WAKE_UP,
+                        DozeLog.REASON_SENSOR_TAP));
+        HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
+                Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
+                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
+                        DozeLog.REASON_SENSOR_TAP));
+
+        for (int i = 0; i < DozeLog.REASONS; i++) {
+            reset(mKeyguardUpdateMonitor);
+            mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
+            if (reasonsWantingAuth.contains(i)) {
+                verify(mKeyguardUpdateMonitor).onAuthInterruptDetected();
+            } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
+                verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected();
+            } else {
+                throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
+                        + " passive auth. Please consider how this pulse reason should behave.");
+            }
+        }
+    }
+
+    @Test
     public void testSetState_changesIsFullScreenUserSwitcherState() {
         mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
         assertFalse(mStatusBar.isFullScreenUserSwitcherState());
@@ -705,7 +744,8 @@
                 NotificationPresenter notificationPresenter,
                 BubbleController bubbleController,
                 NavigationBarController navBarController,
-                AutoHideController autoHideController) {
+                AutoHideController autoHideController,
+                KeyguardUpdateMonitor keyguardUpdateMonitor) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -738,6 +778,7 @@
             mBubbleController = bubbleController;
             mNavigationBarController = navBarController;
             mAutoHideController = autoHideController;
+            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 8172e71..1d0b9b6 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -20,6 +20,7 @@
         package="com.android.vpndialogs">
 
     <uses-permission android:name="android.permission.CONTROL_VPN" />
+    <uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
 
     <application android:label="VpnDialogs"
diff --git a/packages/overlays/FontArbutusSourceOverlay/Android.mk b/packages/overlays/FontArbutusSourceOverlay/Android.mk
deleted file mode 100644
index 23aee55..0000000
--- a/packages/overlays/FontArbutusSourceOverlay/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-#  Copyright 2018, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_RRO_THEME := FontArbutusSource
-LOCAL_CERTIFICATE := platform
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := FontArbutusSourceOverlay
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml b/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml
deleted file mode 100644
index 46c06b9..0000000
--- a/packages/overlays/FontArbutusSourceOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.theme.font.arbutussource"
-    android:versionCode="1"
-    android:versionName="1.0">
-    <overlay android:targetPackage="android"
-        android:category="android.theme.customization.font"
-        android:priority="1"/>
-
-    <application android:label="@string/font_arbutus_source_overlay" android:hasCode="false">
-        <meta-data
-            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
-            android:value="arbutus-slab,source-sans-pro,source-sans-pro-medium" />
-    </application>
-</manifest>
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
deleted file mode 100644
index a6aa64c..0000000
--- a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-     <!-- Name of a font family to use for body text. -->
-    <string name="config_bodyFontFamily" translatable="false">source-sans-pro</string>
-    <!-- Name of a font family to use for medium body text. -->
-    <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-semi-bold</string>
-    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
-    <string name="config_headlineFontFamily" translatable="false">arbutus-slab</string>
-    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
-    <string name="config_headlineFontFamilyMedium" translatable="false">arbutus-slab</string>
-</resources>
-
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml
deleted file mode 100644
index d80eb20..0000000
--- a/packages/overlays/FontArbutusSourceOverlay/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Headline / Body font Arbutus Slab / Source Sans Pro overlay -->
-    <string name="font_arbutus_source_overlay" translatable="false">Arbutus Slab / Source Sans Pro</string>
-</resources>
diff --git a/packages/overlays/FontArvoLatoOverlay/Android.mk b/packages/overlays/FontArvoLatoOverlay/Android.mk
deleted file mode 100644
index 3433ecf..0000000
--- a/packages/overlays/FontArvoLatoOverlay/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-#  Copyright 2018, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_RRO_THEME := FontArvoLato
-LOCAL_CERTIFICATE := platform
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := FontArvoLatoOverlay
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml b/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml
deleted file mode 100644
index 3f2e5de..0000000
--- a/packages/overlays/FontArvoLatoOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.theme.font.arvolato"
-    android:versionCode="1"
-    android:versionName="1.0">
-    <overlay android:targetPackage="android"
-        android:category="android.theme.customization.font"
-        android:priority="1"/>
-
-    <application android:label="@string/font_arvo_lato_overlay" android:hasCode="false">
-        <meta-data
-            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
-            android:value="arvo,arvo-medium,lato,lato-medium" />
-    </application>
-</manifest>
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
deleted file mode 100644
index 4e70d72..0000000
--- a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-     <!-- Name of a font family to use for body text. -->
-    <string name="config_bodyFontFamily" translatable="false">lato</string>
-    <!-- Name of a font family to use for medium body text. -->
-    <string name="config_bodyFontFamilyMedium" translatable="false">lato-bold</string>
-    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
-    <string name="config_headlineFontFamily" translatable="false">arvo</string>
-    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
-    <string name="config_headlineFontFamilyMedium" translatable="false">arvo-bold</string>
-</resources>
-
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/strings.xml b/packages/overlays/FontArvoLatoOverlay/res/values/strings.xml
deleted file mode 100644
index 9ea097f..0000000
--- a/packages/overlays/FontArvoLatoOverlay/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Headline / Body font Arvo / Lato overlay -->
-    <string name="font_arvo_lato_overlay" translatable="false">Arvo / Lato</string>
-</resources>
diff --git a/packages/overlays/FontRubikRubikOverlay/Android.mk b/packages/overlays/FontRubikRubikOverlay/Android.mk
deleted file mode 100644
index 21d617e..0000000
--- a/packages/overlays/FontRubikRubikOverlay/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-#  Copyright 2018, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_RRO_THEME := FontRubikRubik
-LOCAL_CERTIFICATE := platform
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := FontRubikRubikOverlay
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml b/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml
deleted file mode 100644
index 1f28d46..0000000
--- a/packages/overlays/FontRubikRubikOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.theme.font.rubikrubik"
-    android:versionCode="1"
-    android:versionName="1.0">
-    <overlay android:targetPackage="android"
-        android:category="android.theme.customization.font"
-        android:priority="1"/>
-
-    <application android:label="@string/font_rubik_rubik_overlay" android:hasCode="false">
-        <meta-data
-            android:name="android.theme.customization.REQUIRED_SYSTEM_FONTS"
-            android:value="rubik,rubik-medium" />
-    </application>
-</manifest>
diff --git a/packages/overlays/FontRubikRubikOverlay/res/values/config.xml b/packages/overlays/FontRubikRubikOverlay/res/values/config.xml
deleted file mode 100644
index 4f90e29..0000000
--- a/packages/overlays/FontRubikRubikOverlay/res/values/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-     <!-- Name of a font family to use for body text. -->
-    <string name="config_bodyFontFamily" translatable="false">rubik</string>
-    <!-- Name of a font family to use for medium body text. -->
-    <string name="config_bodyFontFamilyMedium" translatable="false">rubik-medium</string>
-    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
-    <string name="config_headlineFontFamily" translatable="false">rubik</string>
-    <!-- Name of the font family used for system surfaces where the font should use medium weight -->
-    <string name="config_headlineFontFamilyMedium" translatable="false">rubik-medium</string>
-</resources>
-
diff --git a/proto/Android.bp b/proto/Android.bp
index f3811bd..817a54d 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -17,3 +17,24 @@
         },
     },
 }
+
+java_library_static {
+    name: "metrics-constants-protos",
+    host_supported: true,
+    proto: {
+        type: "nano",
+    },
+    srcs: ["src/metrics_constants/metrics_constants.proto"],
+    no_framework_libs: true,
+    sdk_version: "system_current",
+    // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+    java_version: "1.8",
+    target: {
+        android: {
+            jarjar_rules: "jarjar-rules.txt",
+        },
+        host: {
+            static_libs: ["libprotobuf-java-nano"],
+        },
+    },
+}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 385931d..76bb24d 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6910,6 +6910,32 @@
     // CATEGORY: NOTIFICATION
     FIELD_NOTIFICATION_CATEGORY = 1641;
 
+    // OPEN: Settings > Settings > Network & internet > Click Mobile network to land on page with
+    // details for a SIM/eSIM mobile network > Click edit icon to bring up a rename dialog.
+    // OS: Q
+    MOBILE_NETWORK_RENAME_DIALOG = 1642;
+
+    // ACTION: Settings > Search Bar > Avatar
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_CLICK_ACCOUNT_AVATAR = 1643;
+
+    // OPEN: Set new password (action intents android.app.action.SET_NEW_PASSWORD or
+    // android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SET_NEW_PASSWORD_ACTIVITY = 1644;
+
+    // ACTION: Set new password (action intent android.app.action.SET_NEW_PASSWORD)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_SET_NEW_PASSWORD = 1645;
+
+    // ACTION: Set new password (action intent android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646;
+
     // ---- End Q Constants, all Q 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 b84736b..2b45b49 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1888,11 +1888,31 @@
     LABEL_BAD = 2;
   }
 
+  enum UsabilityStatsTriggerType {
+    // Default/Invalid event
+    TYPE_UNKNOWN = 0;
+
+    // There is a data stall from tx failures
+    TYPE_DATA_STALL_BAD_TX = 1;
+
+    // There is a data stall from rx failures
+    TYPE_DATA_STALL_TX_WITHOUT_RX = 2;
+
+    // There is a data stall from both tx and rx failures
+    TYPE_DATA_STALL_BOTH = 3;
+
+    // Firmware generated an alert
+    TYPE_FIRMWARE_ALERT = 4;
+  }
+
   // The current wifi usability state
   optional Label label = 1;
 
   // The list of timestamped wifi usability stats
   repeated WifiUsabilityStatsEntry stats = 2;
+
+  // What event triggered WifiUsabilityStats.
+  optional UsabilityStatsTriggerType trigger_type = 3;
 }
 
 message DeviceMobilityStatePnoScanStats {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index f4ac130..fc43882 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -348,8 +348,8 @@
     }
 
     int getRelevantEventTypes() {
-        return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0)
-                | mEventTypes;
+        return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK
+                : AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) | mEventTypes;
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 303734a..87872e8 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -332,7 +332,14 @@
                 // If the user is unlocked, we can start the backup service for it. Otherwise we
                 // will start the service when the user is unlocked as part of its unlock callback.
                 if (getUserManager().isUserUnlocked(userId)) {
-                    startServiceForUser(userId);
+                    // Clear calling identity as initialization enforces the system identity but we
+                    // can be coming from shell.
+                    long oldId = Binder.clearCallingIdentity();
+                    try {
+                        startServiceForUser(userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(oldId);
+                    }
                 }
             } else {
                 try {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 75ee99f..9249c9c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -26,6 +26,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -346,21 +347,16 @@
                 @NonNull ComponentName componentName, @NonNull String sessionId, int flags,
                 @NonNull IResultReceiver result) {
             Preconditions.checkNotNull(activityToken);
-            Preconditions.checkNotNull(componentName);
             Preconditions.checkNotNull(sessionId);
             final int userId = UserHandle.getCallingUserId();
 
-            // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
-            // so we don't pass it on startSession (same for Autofill)
-            final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
-
-            // TODO(b/121260224): get from AM as well
-            final int displayId = 0;
+            final ActivityPresentationInfo activityPresentationInfo = getAmInternal()
+                    .getActivityPresentationInfo(activityToken);
 
             synchronized (mLock) {
                 final ContentCapturePerUserService service = getServiceForUserLocked(userId);
-                service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        sessionId, Binder.getCallingUid(), flags, result);
+                service.startSessionLocked(activityToken, activityPresentationInfo, sessionId,
+                        Binder.getCallingUid(), flags, result);
             }
         }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index bc0e19a..c8a27f1 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -19,6 +19,7 @@
 import static android.service.contentcapture.ContentCaptureService.setClientState;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
+import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR;
 import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
@@ -33,6 +34,7 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.content.ComponentName;
+import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
@@ -171,10 +173,18 @@
     // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
-            @NonNull ComponentName componentName, int taskId, int displayId,
+            @NonNull ActivityPresentationInfo activityPresentationInfo,
             @NonNull String sessionId, int uid, int flags,
             @NonNull IResultReceiver clientReceiver) {
-
+        if (activityPresentationInfo == null) {
+            Slog.w(TAG, "basic activity info is null");
+            setClientState(clientReceiver, STATE_DISABLED | STATE_INTERNAL_ERROR,
+                    /* binder= */ null);
+            return;
+        }
+        final int taskId = activityPresentationInfo.taskId;
+        final int displayId = activityPresentationInfo.displayId;
+        final ComponentName componentName = activityPresentationInfo.componentName;
         final ComponentName serviceComponentName = getServiceComponentName();
         final boolean enabled = isEnabledLocked();
         final String historyItem =
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4408db8..f9de554 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -8,6 +8,7 @@
             "frameworks/native/cmds/dumpstate/binder",
             "system/core/storaged/binder",
             "system/vold/binder",
+            "system/gsid/aidl",
         ],
     },
     srcs: [
@@ -17,6 +18,7 @@
         ":installd_aidl",
         ":storaged_aidl",
         ":vold_aidl",
+        ":gsiservice_aidl",
         ":mediaupdateservice_aidl",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
@@ -48,7 +50,7 @@
         "android.hardware.vibrator-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
-        "android.hidl.manager-V1.0-java",
+        "android.hidl.manager-V1.2-java",
         "netd_aidl_interface-java",
         "netd_event_listener_interface-java",
     ],
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 58263fc..f807543 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3653,6 +3653,20 @@
         mTethering.stopTethering(type);
     }
 
+    /**
+     * Get the latest value of the tethering entitlement check.
+     *
+     * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
+     * out some such apps are observed to abuse this API, change to per-UID limits on this API
+     * if it's really needed.
+     */
+    @Override
+    public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+            boolean showEntitlementUi, String callerPkg) {
+        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+        mTethering.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+    }
+
     // Called when we lose the default network and have no replacement yet.
     // This will automatically be cleared after X seconds or a new default network
     // becomes CONNECTED, whichever happens first.  The timer is started by the
diff --git a/services/core/java/com/android/server/DynamicAndroidService.java b/services/core/java/com/android/server/DynamicAndroidService.java
new file mode 100644
index 0000000..12a3f02
--- /dev/null
+++ b/services/core/java/com/android/server/DynamicAndroidService.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.content.Context;
+import android.content.pm.PackageManager;
+import android.gsi.GsiProgress;
+import android.gsi.IGsiService;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IDynamicAndroidService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+/**
+ * DynamicAndroidService implements IDynamicAndroidService. It provides permission check before
+ * passing requests to gsid
+ */
+public class DynamicAndroidService extends IDynamicAndroidService.Stub implements DeathRecipient {
+    private static final String TAG = "DynamicAndroidService";
+    private static final String NO_SERVICE_ERROR = "no gsiservice";
+
+    private Context mContext;
+    private volatile IGsiService mGsiService;
+
+    DynamicAndroidService(Context context) {
+        mContext = context;
+    }
+
+    private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
+        IBinder binder = ServiceManager.getService("gsiservice");
+        if (binder == null) {
+            throw new RemoteException(NO_SERVICE_ERROR);
+        }
+        /**
+         * The init will restart gsiservice if it crashed and the proxy object will need to be
+         * re-initialized in this case.
+         */
+        binder.linkToDeath(recipient, 0);
+        return IGsiService.Stub.asInterface(binder);
+    }
+
+    /** implements DeathRecipient */
+    @Override
+    public void binderDied() {
+        Slog.w(TAG, "gsiservice died; reconnecting");
+        synchronized (this) {
+            mGsiService = null;
+        }
+    }
+
+    private IGsiService getGsiService() throws RemoteException {
+        checkPermission();
+        synchronized (this) {
+            if (mGsiService == null) {
+                mGsiService = connect(this);
+            }
+            return mGsiService;
+        }
+    }
+
+    private void checkPermission() {
+        if (mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MANAGE_DYNAMIC_ANDROID permission");
+        }
+    }
+
+    @Override
+    public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
+        return getGsiService().startGsiInstall(systemSize, userdataSize, true) == 0;
+    }
+
+    @Override
+    public GsiProgress getInstallationProgress() throws RemoteException {
+        return getGsiService().getInstallProgress();
+    }
+
+    @Override
+    public boolean abort() throws RemoteException {
+        return getGsiService().cancelGsiInstall();
+    }
+
+    @Override
+    public boolean isInUse() throws RemoteException {
+        return getGsiService().isGsiRunning();
+    }
+
+    @Override
+    public boolean isInstalled() throws RemoteException {
+        return getGsiService().isGsiInstalled();
+    }
+
+    @Override
+    public boolean remove() throws RemoteException {
+        return getGsiService().removeGsiInstall();
+    }
+
+    @Override
+    public boolean toggle() throws RemoteException {
+        IGsiService gsiService = getGsiService();
+        if (gsiService.isGsiRunning()) {
+            return gsiService.disableGsiInstall();
+        } else {
+            return gsiService.setGsiBootable() == 0;
+        }
+    }
+
+    @Override
+    public boolean write(byte[] buf) throws RemoteException {
+        return getGsiService().commitGsiChunkFromMemory(buf);
+    }
+
+    @Override
+    public boolean commit() throws RemoteException {
+        return getGsiService().setGsiBootable() == 0;
+    }
+}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index d869734..0ed5beb 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -33,7 +33,7 @@
 # It logs the time remaining before the device would've normally gone to sleep without the request.
 2731 power_soft_sleep_requested (savedwaketimems|2)
 # Power save state has changed. See BatterySaverController.java for the details.
-2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5),(reason|1|5)
+2739 battery_saver_mode (fullPrevOffOrOn|1|5),(adaptivePrevOffOrOn|1|5),(fullNowOffOrOn|1|5),(adaptiveNowOffOrOn|1|5),(interactive|1|5),(features|3|5),(reason|1|5)
 27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
 # Note when the user activity timeout has been overriden by ActivityManagerService
 27391 user_activity_timeout_override (override|2|3)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d2c6354..33c6dd2 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -405,6 +405,7 @@
 
         // initialize in-memory settings values
         onBackgroundThrottleWhitelistChangedLocked();
+        onIgnoreSettingsWhitelistChangedLocked();
     }
 
     @GuardedBy("mLock")
@@ -547,17 +548,16 @@
 
     @GuardedBy("mLock")
     private void onBackgroundThrottleWhitelistChangedLocked() {
-        String setting = Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
-        if (setting == null) {
-            setting = "";
-        }
-
         mBackgroundThrottlePackageWhitelist.clear();
         mBackgroundThrottlePackageWhitelist.addAll(
                 SystemConfig.getInstance().getAllowUnthrottledLocation());
-        mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+
+        String setting = Settings.Global.getString(
+                mContext.getContentResolver(),
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+        if (!TextUtils.isEmpty(setting)) {
+            mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+        }
 
         for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p);
@@ -566,17 +566,16 @@
 
     @GuardedBy("lock")
     private void onIgnoreSettingsWhitelistChangedLocked() {
-        String setting = Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST);
-        if (setting == null) {
-            setting = "";
-        }
-
         mIgnoreSettingsPackageWhitelist.clear();
         mIgnoreSettingsPackageWhitelist.addAll(
                 SystemConfig.getInstance().getAllowIgnoreLocationSettings());
-        mIgnoreSettingsPackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+
+        String setting = Settings.Global.getString(
+                mContext.getContentResolver(),
+                Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST);
+        if (!TextUtils.isEmpty(setting)) {
+            mIgnoreSettingsPackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+        }
 
         for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p);
@@ -2200,7 +2199,7 @@
             return false;
         }
 
-        if (mBackgroundThrottlePackageWhitelist.contains(record.mReceiver.mIdentity.mPackageName)) {
+        if (mIgnoreSettingsPackageWhitelist.contains(record.mReceiver.mIdentity.mPackageName)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 4507193..ec40971 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -247,9 +247,8 @@
                 }
 
                 for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
-                    String packageToReport = packages.get(pIndex).getPackageName();
-                    long packageVersionCode = packages.get(pIndex).getVersionCode();
-                    // Observer that will receive failure for packageToReport
+                    VersionedPackage versionedPackage = packages.get(pIndex);
+                    // Observer that will receive failure for versionedPackage
                     PackageHealthObserver currentObserverToNotify = null;
                     int currentObserverImpact = Integer.MAX_VALUE;
 
@@ -258,9 +257,8 @@
                         ObserverInternal observer = mAllObservers.valueAt(oIndex);
                         PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
                         if (registeredObserver != null
-                                && observer.onPackageFailure(packageToReport)) {
-                            int impact = registeredObserver.onHealthCheckFailed(packageToReport,
-                                    packageVersionCode);
+                                && observer.onPackageFailure(versionedPackage.getPackageName())) {
+                            int impact = registeredObserver.onHealthCheckFailed(versionedPackage);
                             if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
                                     && impact < currentObserverImpact) {
                                 currentObserverToNotify = registeredObserver;
@@ -271,7 +269,7 @@
 
                     // Execute action with least user impact
                     if (currentObserverToNotify != null) {
-                        currentObserverToNotify.execute(packageToReport, packageVersionCode);
+                        currentObserverToNotify.execute(versionedPackage);
                     }
                 }
             }
@@ -310,19 +308,19 @@
     /** Register instances of this interface to receive notifications on package failure. */
     public interface PackageHealthObserver {
         /**
-         * Called when health check fails for the {@code packageName}.
+         * Called when health check fails for the {@code versionedPackage}.
          *
          * @return any one of {@link PackageHealthObserverImpact} to express the impact
          * to the user on {@link #execute}
          */
-        @PackageHealthObserverImpact int onHealthCheckFailed(String packageName, long versionCdoe);
+        @PackageHealthObserverImpact int onHealthCheckFailed(VersionedPackage versionedPackage);
 
         /**
          * Executes mitigation for {@link #onHealthCheckFailed}.
          *
          * @return {@code true} if action was executed successfully, {@code false} otherwise
          */
-        boolean execute(String packageName, long versionCode);
+        boolean execute(VersionedPackage versionedPackage);
 
         // TODO(zezeozue): Ensure uniqueness?
         /**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cecd55a3..f2329d3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1541,10 +1541,6 @@
         mCallbacks = new Callbacks(FgThread.get().getLooper());
         mLockPatternUtils = new LockPatternUtils(mContext);
 
-        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
-        mUmInternal = LocalServices.getService(UserManagerInternal.class);
-        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
-
         HandlerThread hthread = new HandlerThread(TAG);
         hthread.start();
         mHandler = new StorageManagerServiceHandler(hthread.getLooper());
@@ -1662,6 +1658,19 @@
     }
 
     private void servicesReady() {
+        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+        mUmInternal = LocalServices.getService(UserManagerInternal.class);
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+
+        mIPackageManager = IPackageManager.Stub.asInterface(
+                ServiceManager.getService("package"));
+        mIAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        try {
+            mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
+        } catch (RemoteException e) {
+        }
+
         synchronized (mLock) {
             final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage();
             if (mLastIsolatedStorage == thisIsolatedStorage) {
@@ -1734,14 +1743,6 @@
                 .registerScreenObserver(this);
 
         mSystemReady = true;
-        mIPackageManager = IPackageManager.Stub.asInterface(
-                ServiceManager.getService("package"));
-        mIAppOpsService = IAppOpsService.Stub.asInterface(
-                ServiceManager.getService(Context.APP_OPS_SERVICE));
-        try {
-            mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
-        } catch (RemoteException e) {
-        }
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
 
diff --git a/services/core/java/com/android/server/ThreadPriorityBooster.java b/services/core/java/com/android/server/ThreadPriorityBooster.java
index 53e8ce4..f74a4385 100644
--- a/services/core/java/com/android/server/ThreadPriorityBooster.java
+++ b/services/core/java/com/android/server/ThreadPriorityBooster.java
@@ -43,9 +43,9 @@
 
     public void boost() {
         final int tid = myTid();
-        final int prevPriority = getThreadPriority(tid);
         final PriorityState state = mThreadState.get();
         if (state.regionCounter == 0) {
+            final int prevPriority = getThreadPriority(tid);
             state.prevPriority = prevPriority;
             if (prevPriority > mBoostToPriority) {
                 setThreadPriority(tid, mBoostToPriority);
@@ -60,9 +60,11 @@
     public void reset() {
         final PriorityState state = mThreadState.get();
         state.regionCounter--;
-        final int currentPriority = getThreadPriority(myTid());
-        if (state.regionCounter == 0 && state.prevPriority != currentPriority) {
-            setThreadPriority(myTid(), state.prevPriority);
+        if (state.regionCounter == 0) {
+            final int currentPriority = getThreadPriority(myTid());
+            if (state.prevPriority != currentPriority) {
+                setThreadPriority(myTid(), state.prevPriority);
+            }
         }
     }
 
@@ -77,9 +79,11 @@
         mBoostToPriority = priority;
         final PriorityState state = mThreadState.get();
         final int tid = myTid();
-        final int prevPriority = getThreadPriority(tid);
-        if (state.regionCounter != 0 && prevPriority != priority) {
-            setThreadPriority(tid, priority);
+        if (state.regionCounter != 0) {
+            final int prevPriority = getThreadPriority(tid);
+            if (prevPriority != priority) {
+                setThreadPriority(tid, priority);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index f6e698f..545baa0 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -593,7 +593,7 @@
                 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-                        && vib.isHapticFeedback()) {
+                        && !vib.isNotification() && !vib.isRingtone()) {
                     Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
                             + uid + " is background");
                     return;
@@ -1454,6 +1454,17 @@
 
         private final IBinder mToken;
 
+        private final class CommonOptions {
+            public boolean force = false;
+            public void check(String opt) {
+                switch (opt) {
+                    case "-f":
+                        force = true;
+                        break;
+                }
+            }
+        }
+
         private VibratorShellCommand(IBinder token) {
             mToken = token;
         }
@@ -1473,11 +1484,11 @@
             return handleDefaultCommands(cmd);
         }
 
-        private boolean checkDoNotDisturb() {
+        private boolean checkDoNotDisturb(CommonOptions opts) {
             try {
                 final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
                         Settings.Global.ZEN_MODE);
-                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                if (zenMode != Settings.Global.ZEN_MODE_OFF && !opts.force) {
                     try (PrintWriter pw = getOutPrintWriter();) {
                         pw.print("Ignoring because device is on DND mode ");
                         pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
@@ -1495,7 +1506,14 @@
         private int runVibrate() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
             try {
-                if (checkDoNotDisturb()) {
+                CommonOptions commonOptions = new CommonOptions();
+
+                String opt;
+                while ((opt = getNextOption()) != null) {
+                    commonOptions.check(opt);
+                }
+
+                if (checkDoNotDisturb(commonOptions)) {
                     return 0;
                 }
 
@@ -1518,13 +1536,10 @@
         private int runWaveform() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
             try {
-                if (checkDoNotDisturb()) {
-                    return 0;
-                }
-
                 String description = "Shell command";
                 int repeat = -1;
                 ArrayList<Integer> amplitudesList = null;
+                CommonOptions commonOptions = new CommonOptions();
 
                 String opt;
                 while ((opt = getNextOption()) != null) {
@@ -1540,9 +1555,16 @@
                                 amplitudesList = new ArrayList<Integer>();
                             }
                             break;
+                        default:
+                            commonOptions.check(opt);
+                            break;
                     }
                 }
 
+                if (checkDoNotDisturb(commonOptions)) {
+                    return 0;
+                }
+
                 ArrayList<Long> timingsList = new ArrayList<Long>();
 
                 String arg;
@@ -1574,7 +1596,14 @@
         private int runPrebaked() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
             try {
-                if (checkDoNotDisturb()) {
+                CommonOptions commonOptions = new CommonOptions();
+
+                String opt;
+                while ((opt = getNextOption()) != null) {
+                    commonOptions.check(opt);
+                }
+
+                if (checkDoNotDisturb(commonOptions)) {
                     return 0;
                 }
 
@@ -1618,6 +1647,8 @@
                 pw.println("    (Do Not Disturb) mode.");
                 pw.println("  cancel");
                 pw.println("    Cancels any active vibration");
+                pw.println("Common Options:");
+                pw.println("  -f - Force. Ignore Do Not Disturb setting.");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 15de3de..9510f59 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -49,6 +49,9 @@
     // Connection to use the UI introspection APIs.
     IUiAutomationConnection mUiAutomationConnection;
 
+    // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission
+    boolean mHasBackgroundActivityStartsPermission;
+
     // As given to us
     Bundle mArguments;
 
@@ -117,6 +120,8 @@
             pw.print(prefix); pw.print("mUiAutomationConnection=");
             pw.println(mUiAutomationConnection);
         }
+        pw.print("mHasBackgroundActivityStartsPermission=");
+        pw.println(mHasBackgroundActivityStartsPermission);
         pw.print(prefix); pw.print("mArguments=");
         pw.println(mArguments);
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d95604e..2f1f91e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
@@ -218,7 +219,7 @@
                     if (DEBUG_FOREGROUND_SERVICE) {
                         Slog.i(TAG, "  Stopping fg for service " + r);
                     }
-                    setServiceForegroundInnerLocked(r, 0, null, 0);
+                    setServiceForegroundInnerLocked(r, 0, null, 0, 0);
                 }
             }
         }
@@ -914,13 +915,13 @@
     }
 
     public void setServiceForegroundLocked(ComponentName className, IBinder token,
-            int id, Notification notification, int flags) {
+            int id, Notification notification, int flags, int foregroundServiceType) {
         final int userId = UserHandle.getCallingUserId();
         final long origId = Binder.clearCallingIdentity();
         try {
             ServiceRecord r = findServiceLocked(className, token, userId);
             if (r != null) {
-                setServiceForegroundInnerLocked(r, id, notification, flags);
+                setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -1211,7 +1212,7 @@
      * @param id Notification ID.  Zero === exit foreground state for the given service.
      */
     private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
-            Notification notification, int flags) {
+            Notification notification, int flags, int foregroundServiceType) {
         if (id != 0) {
             if (notification == null) {
                 throw new IllegalArgumentException("null notification");
@@ -1244,13 +1245,20 @@
                             android.Manifest.permission.FOREGROUND_SERVICE,
                             r.app.pid, r.appInfo.uid, "startForeground");
                 }
-                if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
-                    if (r.serviceInfo.getForegroundServiceType()
-                            == ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED) {
-                        // STOPSHIP(b/120611119): replace log message with SecurityException.
-                        Slog.w(TAG, "missing foregroundServiceType attribute in "
-                                + "service element of manifest file");
-                    }
+
+                int manifestType = r.serviceInfo.getForegroundServiceType();
+                // If passed in foreground service type is FOREGROUND_SERVICE_TYPE_MANIFEST,
+                // consider it is the same as manifest foreground service type.
+                if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {
+                    foregroundServiceType = manifestType;
+                }
+                // Check the passed in foreground service type flags is a subset of manifest
+                // foreground service type flags.
+                if ((foregroundServiceType & manifestType) != foregroundServiceType) {
+                    // STOPSHIP(b/120611119): replace log message with IllegalArgumentException.
+                    Slog.w(TAG, "foregroundServiceType must be a subset of "
+                            + "foregroundServiceType attribute in "
+                            + "service element of manifest file");
                 }
             }
             boolean alreadyStartedOp = false;
@@ -1307,6 +1315,7 @@
                     }
                     notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                     r.foregroundNoti = notification;
+                    r.foregroundServiceType = foregroundServiceType;
                     if (!r.isForeground) {
                         final ServiceMap smap = getServiceMapLocked(r.userId);
                         if (smap != null) {
@@ -1443,7 +1452,7 @@
             ServiceRecord sr = proc.services.valueAt(i);
             if (sr.isForeground || sr.fgRequired) {
                 anyForeground = true;
-                fgServiceTypes |= sr.serviceInfo.mForegroundServiceType;
+                fgServiceTypes |= sr.foregroundServiceType;
                 break;
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bec7386..5538e49 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.REMOVE_TASKS;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
@@ -120,6 +121,7 @@
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -197,6 +199,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
 import android.content.pm.IPackageDataObserver;
@@ -283,6 +286,7 @@
 import android.util.TimingsTraceLog;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
@@ -548,6 +552,11 @@
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
     boolean mUseFifoUiScheduling = false;
 
+    // Use an offload queue for long broadcasts, e.g. BOOT_COMPLETED.
+    // For simplicity, since we statically declare the size of the array of BroadcastQueues,
+    // we still create this new offload queue, but never ever put anything on it.
+    boolean mEnableOffloadQueue;
+
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     BroadcastQueue mOffloadBroadcastQueue;
@@ -2217,7 +2226,7 @@
         mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
         final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
         mProcessList.init(this, activeUids);
-        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, new Object());
 
         mIntentFirewall = hasHandlerThread
                 ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
@@ -2265,7 +2274,7 @@
         mConstants = new ActivityManagerConstants(this, mHandler);
         final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
         mProcessList.init(this, activeUids);
-        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, atm.getGlobalLock());
 
         // Broadcast policy parameters
         final BroadcastConstants foreConstants = new BroadcastConstants(
@@ -2282,6 +2291,9 @@
         // by default, no "slow" policy in this queue
         offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
 
+        mEnableOffloadQueue = SystemProperties.getBoolean(
+                "persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
+
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", foreConstants, false);
         mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
@@ -3091,7 +3103,7 @@
                 } else {
                     UidRecord validateUid = mValidateUids.get(item.uid);
                     if (validateUid == null) {
-                        validateUid = new UidRecord(item.uid, mAtmInternal);
+                        validateUid = new UidRecord(item.uid);
                         mValidateUids.put(item.uid, validateUid);
                     }
                     if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
@@ -3576,6 +3588,9 @@
         if (tracesFile == null) {
             return null;
         }
+        if (DEBUG_ANR) {
+            Slog.d(TAG, "Dumping to " + tracesFile.getAbsolutePath());
+        }
 
         dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
         return tracesFile;
@@ -4495,6 +4510,7 @@
         // next app record if we are emulating process with anonymous threads.
         ProcessRecord app;
         long startTime = SystemClock.uptimeMillis();
+        long bindApplicationTimeMillis;
         if (pid != MY_PID && pid >= 0) {
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
@@ -4732,6 +4748,7 @@
             }
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
+            bindApplicationTimeMillis = SystemClock.elapsedRealtime();
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (app.isolatedEntryPoint != null) {
@@ -4835,7 +4852,7 @@
             try {
                 thread.scheduleCreateBackupAgent(backupTarget.appInfo,
                         compatibilityInfoForPackage(backupTarget.appInfo),
-                        backupTarget.backupMode);
+                        backupTarget.backupMode, backupTarget.userId);
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
                 badApp = true;
@@ -4853,6 +4870,17 @@
             checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
         }
 
+        StatsLog.write(
+                StatsLog.PROCESS_START_TIME,
+                app.info.uid,
+                app.pid,
+                app.info.packageName,
+                StatsLog.PROCESS_START_TIME__TYPE__COLD,
+                app.startTime,
+                (int) (bindApplicationTimeMillis - app.startTime),
+                (int) (SystemClock.elapsedRealtime() - app.startTime),
+                app.hostingType,
+                (app.hostingNameStr != null ? app.hostingNameStr : ""));
         return true;
     }
 
@@ -13529,9 +13557,10 @@
 
     @Override
     public void setServiceForeground(ComponentName className, IBinder token,
-            int id, Notification notification, int flags) {
+            int id, Notification notification, int flags, int foregroundServiceType) {
         synchronized(this) {
-            mServices.setServiceForegroundLocked(className, token, id, notification, flags);
+            mServices.setServiceForegroundLocked(className, token, id, notification, flags,
+                    foregroundServiceType);
         }
     }
 
@@ -13670,18 +13699,26 @@
     // Cause the target app to be launched if necessary and its backup agent
     // instantiated.  The backup agent will invoke backupAgentCreated() on the
     // activity manager to announce its creation.
-    public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
+    public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId) {
         if (DEBUG_BACKUP) {
-            Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode="
-                    + backupMode + " userId=" + userId + " callingUid = " + Binder.getCallingUid()
+            Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
+                    + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid()
                     + " uid = " + Process.myUid());
         }
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
 
+        // The instantiatedUserId is the user of the process the backup agent is started in. This is
+        // different from the targetUserId which is the user whose data is to be backed up or
+        // restored. This distinction is important for system-process packages that live in the
+        // system user's process but backup/restore data for non-system users.
+        // TODO (b/123688746): Handle all system-process packages with singleton check.
+        final int instantiatedUserId =
+                PLATFORM_PACKAGE_NAME.equals(packageName) ? UserHandle.USER_SYSTEM : targetUserId;
+
         IPackageManager pm = AppGlobals.getPackageManager();
         ApplicationInfo app = null;
         try {
-            app = pm.getApplicationInfo(packageName, STOCK_PM_FLAGS, userId);
+            app = pm.getApplicationInfo(packageName, STOCK_PM_FLAGS, instantiatedUserId);
         } catch (RemoteException e) {
             // can't happen; package manager is process-local
         }
@@ -13705,7 +13742,7 @@
                         + app.packageName + ": " + e);
             }
 
-            BackupRecord r = new BackupRecord(app, backupMode);
+            BackupRecord r = new BackupRecord(app, backupMode, targetUserId);
             ComponentName hostingName =
                     (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
                             ? new ComponentName(app.packageName, app.backupAgentName)
@@ -13727,10 +13764,10 @@
                 proc.inFullBackup = true;
             }
             r.app = proc;
-            final BackupRecord backupTarget = mBackupTargets.get(userId);
+            final BackupRecord backupTarget = mBackupTargets.get(targetUserId);
             oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
             newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
-            mBackupTargets.put(userId, r);
+            mBackupTargets.put(targetUserId, r);
 
             // Try not to kill the process during backup
             updateOomAdjLocked(proc, true);
@@ -13741,7 +13778,7 @@
                 if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
                 try {
                     proc.thread.scheduleCreateBackupAgent(app,
-                            compatibilityInfoForPackage(app), backupMode);
+                            compatibilityInfoForPackage(app), backupMode, targetUserId);
                 } catch (RemoteException e) {
                     // Will time out on the backup manager side
                 }
@@ -13781,16 +13818,18 @@
 
     // A backup agent has just come up
     @Override
-    public void backupAgentCreated(String agentPackageName, IBinder agent) {
-        final int callingUserId = UserHandle.getCallingUserId();
+    public void backupAgentCreated(String agentPackageName, IBinder agent, int userId) {
+        // Resolve the target user id and enforce permissions.
+        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, /* allowAll */ false, ALLOW_FULL_ONLY, "backupAgentCreated", null);
         if (DEBUG_BACKUP) {
             Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent
-                    + " callingUserId = " + callingUserId + " callingUid = "
-                    + Binder.getCallingUid() + " uid = " + Process.myUid());
+                    + " callingUserId = " + UserHandle.getCallingUserId() + " userId = " + userId
+                    + " callingUid = " + Binder.getCallingUid() + " uid = " + Process.myUid());
         }
 
         synchronized(this) {
-            final BackupRecord backupTarget = mBackupTargets.get(callingUserId);
+            final BackupRecord backupTarget = mBackupTargets.get(userId);
             String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
             if (!agentPackageName.equals(backupAppName)) {
                 Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
@@ -13802,7 +13841,7 @@
         try {
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
-            bm.agentConnectedForUser(callingUserId, agentPackageName, agent);
+            bm.agentConnectedForUser(userId, agentPackageName, agent);
         } catch (RemoteException e) {
             // can't happen; the backup manager service is local
         } catch (Exception e) {
@@ -13855,7 +13894,7 @@
                 if (proc.thread != null) {
                     try {
                         proc.thread.scheduleDestroyBackupAgent(appInfo,
-                                compatibilityInfoForPackage(appInfo));
+                                compatibilityInfoForPackage(appInfo), userId);
                     } catch (Exception e) {
                         Slog.e(TAG, "Exception when unbinding backup agent:");
                         e.printStackTrace();
@@ -14089,7 +14128,8 @@
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                             null, -1, -1, false, null, null, OP_NONE, null, receivers,
-                            null, 0, null, null, false, true, true, -1, false);
+                            null, 0, null, null, false, true, true, -1, false,
+                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
                 }
@@ -14474,6 +14514,8 @@
             }
         }
 
+        boolean timeoutExempt = false;
+
         if (action != null) {
             if (getBackgroundLaunchBroadcasts().contains(action)) {
                 if (DEBUG_BACKGROUND_CHECK) {
@@ -14699,6 +14741,9 @@
                     Log.w(TAG, "Broadcast " + action
                             + " no longer supported. It will not be delivered.");
                     return ActivityManager.BROADCAST_SUCCESS;
+                case Intent.ACTION_PRE_BOOT_COMPLETED:
+                    timeoutExempt = true;
+                    break;
             }
 
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
@@ -14841,7 +14886,7 @@
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                     resultCode, resultData, resultExtras, ordered, sticky, false, userId,
-                    allowBackgroundActivityStarts);
+                    allowBackgroundActivityStarts, timeoutExempt);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             final boolean replaced = replacePending
                     && (queue.replaceParallelBroadcastLocked(r) != null);
@@ -14938,7 +14983,7 @@
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                     resultData, resultExtras, ordered, sticky, false, userId,
-                    allowBackgroundActivityStarts);
+                    allowBackgroundActivityStarts, timeoutExempt);
 
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
 
@@ -15195,7 +15240,9 @@
             IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
             int userId, String abiOverride) {
         enforceNotIsolatedCaller("startInstrumentation");
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        userId = mUserController.handleIncomingUser(callingPid, callingUid,
                 userId, false, ALLOW_FULL_ONLY, "startInstrumentation", null);
         // Refuse possible leaked file descriptors
         if (arguments != null && arguments.hasFileDescriptors()) {
@@ -15260,6 +15307,9 @@
             activeInstr.mWatcher = watcher;
             activeInstr.mUiAutomationConnection = uiAutomationConnection;
             activeInstr.mResultClass = className;
+            activeInstr.mHasBackgroundActivityStartsPermission = checkPermission(
+                    START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+                            == PackageManager.PERMISSION_GRANTED;
 
             boolean disableHiddenApiChecks = ai.usesNonSdkApi()
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
@@ -17622,6 +17672,19 @@
         }
 
         @Override
+        public ActivityPresentationInfo getActivityPresentationInfo(IBinder token) {
+            int displayId = Display.INVALID_DISPLAY;
+            try {
+                displayId = mActivityTaskManager.getActivityDisplayId(token);
+            } catch (RemoteException e) {
+            }
+
+            return new ActivityPresentationInfo(mActivityTaskManager.getTaskForActivity(token,
+                    /*onlyRoot=*/ false), displayId,
+                    mActivityTaskManager.getActivityClassForToken(token));
+        }
+
+        @Override
         public void setBooting(boolean booting) {
             mBooting = booting;
         }
@@ -18383,6 +18446,6 @@
     }
 
     private boolean isOnOffloadQueue(int flags) {
-        return ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0);
+        return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 740c0da..4f6f9fd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1815,7 +1815,7 @@
 
     int runTrackAssociations(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
-                "registerUidObserver()");
+                "runTrackAssociations()");
         synchronized (mInternal) {
             if (!mInternal.mTrackingAssociations) {
                 mInternal.mTrackingAssociations = true;
@@ -1829,7 +1829,7 @@
 
     int runUntrackAssociations(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
-                "registerUidObserver()");
+                "runUntrackAssociations()");
         synchronized (mInternal) {
             if (mInternal.mTrackingAssociations) {
                 mInternal.mTrackingAssociations = false;
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index bb55ec31..1118014 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -19,6 +19,7 @@
 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
@@ -45,6 +46,7 @@
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Random;
 
 public final class AppCompactor {
 
@@ -65,6 +67,8 @@
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
+    // The sampling rate to push app compaction events into statsd for upload.
+    @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
 
     @VisibleForTesting
     interface PropertyChangedCallbackForTest {
@@ -104,6 +108,8 @@
                                 || KEY_COMPACT_THROTTLE_3.equals(name)
                                 || KEY_COMPACT_THROTTLE_4.equals(name)) {
                             updateCompactionThrottles();
+                        } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
+                            updateStatsdSampleRate();
                         }
                     }
                     if (mTestCallback != null) {
@@ -116,21 +122,25 @@
 
     // Configured by phenotype. Updates from the server take effect immediately.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting String mCompactActionSome =
+    @VisibleForTesting volatile String mCompactActionSome =
             compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting String mCompactActionFull =
+    @VisibleForTesting volatile String mCompactActionFull =
             compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
+    @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
+    @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
+    @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
+    @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
     @GuardedBy("mPhenotypeFlagLock")
-    private boolean mUseCompaction = DEFAULT_USE_COMPACTION;
+    private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
+
+    private final Random mRandom = new Random();
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting volatile float mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
 
     // Handler on which compaction runs.
     private Handler mCompactionHandler;
@@ -158,6 +168,7 @@
             updateUseCompaction();
             updateCompactionActions();
             updateCompactionThrottles();
+            updateStatsdSampleRate();
         }
     }
 
@@ -181,6 +192,7 @@
             pw.println("  " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
             pw.println("  " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
             pw.println("  " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull);
+            pw.println("  " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mStatsdSampleRate);
         }
     }
 
@@ -289,6 +301,19 @@
         }
     }
 
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateStatsdSampleRate() {
+        String sampleRateFlag = DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE);
+        try {
+            mStatsdSampleRate = TextUtils.isEmpty(sampleRateFlag)
+                    ? DEFAULT_STATSD_SAMPLE_RATE : Float.parseFloat(sampleRateFlag);
+        } catch (NumberFormatException e) {
+            mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
+        }
+        mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate));
+    }
+
     @VisibleForTesting
     static String compactActionIntToString(int action) {
         switch(action) {
@@ -385,11 +410,16 @@
                                 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
                                 rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
                                 lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
-                        StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
-                                rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
-                                rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
-                                lastCompactAction, lastCompactTime, msg.arg1,
-                                ActivityManager.processStateAmToProto(msg.arg2));
+                        // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
+                        // on every single compaction for a flag that will seldom change and the
+                        // impact of reading the wrong value here is low.
+                        if (mRandom.nextFloat() < mStatsdSampleRate) {
+                            StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+                                    rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+                                    rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+                                    lastCompactAction, lastCompactTime, msg.arg1,
+                                    ActivityManager.processStateAmToProto(msg.arg2));
+                        }
                         synchronized (mAm) {
                             proc.lastCompactTime = end;
                             proc.lastCompactAction = pendingAction;
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 40ad383..37b4be4 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -28,14 +28,16 @@
     
     String stringName;                     // cached toString() output
     final ApplicationInfo appInfo;         // information about BackupAgent's app
+    final int userId;                      // user for which backup is performed
     final int backupMode;                  // full backup / incremental / restore
     ProcessRecord app;                     // where this agent is running or null
 
     // ----- Implementation -----
 
-    BackupRecord(ApplicationInfo _appInfo, int _backupMode) {
+    BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId) {
         appInfo = _appInfo;
         backupMode = _backupMode;
+        userId = _userId;
     }
 
     public String toString() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 64a36ef..a11ebfd 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -450,7 +450,7 @@
         if (state == BroadcastRecord.IDLE) {
             Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
         }
-        if (r.allowBackgroundActivityStarts) {
+        if (r.allowBackgroundActivityStarts && r.curApp != null) {
             r.curApp.removeAllowBackgroundActivityStartsToken(r);
          }
         // If we're abandoning this broadcast before any receivers were actually spun up,
@@ -461,14 +461,21 @@
 
         // if this receiver was slow, impose deferral policy on the app.  This will kick in
         // when processNextBroadcastLocked() next finds this uid as a receiver identity.
-        if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
-            if (DEBUG_BROADCAST_DEFERRAL) {
-                Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+        if (!r.timeoutExempt) {
+            if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+                if (DEBUG_BROADCAST_DEFERRAL) {
+                    Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+                }
+                if (r.curApp != null) {
+                    mDispatcher.startDeferring(r.curApp.uid);
+                } else {
+                    Slog.d(TAG, "finish receiver curApp is null? " + r);
+                }
             }
-            if (r.curApp != null) {
-                mDispatcher.startDeferring(r.curApp.uid);
-            } else {
-                Slog.d(TAG, "finish receiver curApp is null? " + r);
+        } else {
+            if (DEBUG_BROADCAST_DEFERRAL) {
+                Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
+                        + " is exempt from deferral policy");
             }
         }
 
@@ -1008,12 +1015,11 @@
             // detection, we catch "hung" broadcasts here, discard them,
             // and continue to make progress.
             //
-            // This is only done if the system is ready so that PRE_BOOT_COMPLETED
-            // receivers don't get executed with timeouts. They're intended for
-            // one time heavy lifting after system upgrades and can take
-            // significant amounts of time.
+            // This is only done if the system is ready so that early-stage receivers
+            // don't get executed with timeouts; and of course other timeout-
+            // exempt broadcasts are ignored.
             int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-            if (mService.mProcessesReady && r.dispatchTime > 0) {
+            if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
                 if ((numReceivers > 0) &&
                         (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
                     Slog.w(TAG, "Hung broadcast ["
@@ -1619,9 +1625,17 @@
         BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
         if (fromMsg) {
             if (!mService.mProcessesReady) {
-                // Only process broadcast timeouts if the system is ready. That way
-                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
-                // to do heavy lifting for system up.
+                // Only process broadcast timeouts if the system is ready; some early
+                // broadcasts do heavy work setting up system facilities
+                return;
+            }
+
+            // If the broadcast is generally exempt from timeout tracking, we're done
+            if (r.timeoutExempt) {
+                if (DEBUG_BROADCAST) {
+                    Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+                            + r.intent.getAction());
+                }
                 return;
             }
 
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index d9e03f8..fa9b79d 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -74,6 +74,7 @@
     long dispatchClockTime; // the clock time the dispatch started
     long receiverTime;      // when current receiver started for timeouts.
     long finishTime;        // when we finished the broadcast.
+    boolean timeoutExempt;  // true if this broadcast is not subject to receiver timeouts
     int resultCode;         // current result code value.
     String resultData;      // current result data value.
     Bundle resultExtras;    // current result extra data values.
@@ -236,7 +237,7 @@
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
             boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
-            boolean _allowBackgroundActivityStarts) {
+            boolean _allowBackgroundActivityStarts, boolean _timeoutExempt) {
         if (_intent == null) {
             throw new NullPointerException("Can't construct with a null intent");
         }
@@ -266,6 +267,7 @@
         nextReceiver = 0;
         state = IDLE;
         allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
+        timeoutExempt = _timeoutExempt;
     }
 
     /**
@@ -310,6 +312,7 @@
         manifestSkipCount = from.manifestSkipCount;
         queue = from.queue;
         allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
+        timeoutExempt = from.timeoutExempt;
     }
 
     /**
@@ -345,7 +348,7 @@
                 callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                 requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
                 resultData, resultExtras, ordered, sticky, initialSticky, userId,
-                allowBackgroundActivityStarts);
+                allowBackgroundActivityStarts, timeoutExempt);
 
         return split;
     }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 6e8646e..4985c52 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -127,8 +127,18 @@
     private final ActivityManagerService mService;
     private final ProcessList mProcessList;
 
-    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
+    /**
+     * Used to lock {@link #updateOomAdjImpl} for state consistency. It also reduces frequency lock
+     * and unlock when getting and setting value to {@link ProcessRecord#mWindowProcessController}.
+     * Note it is declared as Object type so the locked-region-code-injection won't wrap the
+     * unnecessary priority booster.
+     */
+    private final Object mAtmGlobalLock;
+
+    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
+            Object atmGlobalLock) {
         mService = service;
+        mAtmGlobalLock = atmGlobalLock;
         mProcessList = processList;
         mActiveUids = activeUids;
 
@@ -186,6 +196,13 @@
 
     @GuardedBy("mService")
     final void updateOomAdjLocked() {
+        synchronized (mAtmGlobalLock) {
+            updateOomAdjImpl();
+        }
+    }
+
+    @GuardedBy({"mService", "mAtmGlobalLock"})
+    private void updateOomAdjImpl() {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "updateOomAdj");
         mService.mOomAdjProfiler.oomAdjStarted();
         final ProcessRecord TOP_APP = mService.getTopAppLocked();
@@ -534,6 +551,7 @@
                 uidRec.setProcState = uidRec.getCurProcState();
                 uidRec.setWhitelist = uidRec.curWhitelist;
                 uidRec.setIdle = uidRec.idle;
+                mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
                 mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
                 mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
                 if (uidRec.foregroundServices) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4aa71f9..f90c0ca 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -58,7 +58,6 @@
 import android.graphics.Point;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
-import android.net.Uri;
 import android.os.AppZygote;
 import android.os.Binder;
 import android.os.Build;
@@ -1481,6 +1480,9 @@
                     mService.mSafeMode == true) {
                 runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
             }
+            if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0) {
+                runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
+            }
             if ("1".equals(SystemProperties.get("debug.checkjni"))) {
                 runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
             }
@@ -1507,7 +1509,7 @@
                 mService.mNativeDebuggingApp = null;
             }
 
-            if (app.info.isCodeIntegrityPreferred()
+            if (app.info.isEmbeddedDexUsed()
                     || (app.info.isPrivilegedApp()
                         && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
@@ -2197,7 +2199,7 @@
         }
         UidRecord uidRec = mActiveUids.get(proc.uid);
         if (uidRec == null) {
-            uidRec = new UidRecord(proc.uid, mService.mAtmInternal);
+            uidRec = new UidRecord(proc.uid);
             // This is the first appearance of the uid, report it now!
             if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                     "Creating new process uid: " + uidRec);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7ae77d5..5dccaf1 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -655,10 +655,6 @@
         return mWindowProcessController.hasActivities();
     }
 
-    void clearActivities() {
-        mWindowProcessController.clearActivities();
-    }
-
     boolean hasActivitiesOrRecentTasks() {
         return mWindowProcessController.hasActivitiesOrRecentTasks();
     }
@@ -667,10 +663,6 @@
         return mWindowProcessController.hasRecentTasks();
     }
 
-    void clearRecentTasks() {
-        mWindowProcessController.clearRecentTasks();
-    }
-
     /**
      * This method returns true if any of the activities within the process record are interesting
      * to the user. See HistoryRecord.isInterestingToUserLocked()
@@ -1180,7 +1172,9 @@
 
     void setActiveInstrumentation(ActiveInstrumentation instr) {
         mInstr = instr;
-        mWindowProcessController.setInstrumenting(instr != null);
+        boolean isInstrumenting = instr != null;
+        mWindowProcessController.setInstrumenting(isInstrumenting,
+                isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
     }
 
     ActiveInstrumentation getActiveInstrumentation() {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index da5ce1c..abc1066a 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -16,10 +16,8 @@
 
 package com.android.server.am;
 
-import com.android.internal.app.procstats.ServiceState;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.LocalServices;
-import com.android.server.notification.NotificationManagerInternal;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -44,6 +42,11 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
+
+import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriPermissionOwner;
 
@@ -52,9 +55,6 @@
 import java.util.List;
 import java.util.Objects;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
 /**
  * A running application service.
  */
@@ -103,6 +103,7 @@
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
+    int foregroundServiceType; // foreground service types.
     long lastActivity;      // last time there was some activity on the service.
     long startingBgTimeout;  // time at which we scheduled this for a delayed start.
     boolean startRequested; // someone explicitly called start?
@@ -722,7 +723,7 @@
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
                         ams.setServiceForeground(instanceName, ServiceRecord.this,
-                                0, null, 0);
+                                0, null, 0, 0);
                         ams.crashApplication(appUid, appPid, localPackageName, -1,
                                 "Bad notification for startForeground: " + e);
                     }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 6cb1097..22a7de7 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -26,7 +26,6 @@
 import android.util.proto.ProtoUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.server.wm.ActivityTaskManagerInternal;
 
 /**
  * Overall information about a uid that has actively running processes.
@@ -43,7 +42,6 @@
     boolean idle;
     boolean setIdle;
     int numProcs;
-    final ActivityTaskManagerInternal mAtmInternal;
 
     /**
      * Sequence number associated with the {@link #mCurProcState}. This is incremented using
@@ -117,10 +115,9 @@
     ChangeItem pendingChange;
     int lastReportedChange;
 
-    public UidRecord(int _uid, ActivityTaskManagerInternal atmInternal) {
+    public UidRecord(int _uid) {
         uid = _uid;
         idle = true;
-        mAtmInternal = atmInternal;
         reset();
     }
 
@@ -130,9 +127,6 @@
 
     public void setCurProcState(int curProcState) {
         mCurProcState = curProcState;
-        if (mAtmInternal != null) {
-            mAtmInternal.onUidProcStateChanged(uid, curProcState);
-        }
     }
 
     public void reset() {
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 5b469fe..2061b26 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -16,9 +16,9 @@
 
 package com.android.server.attention;
 
+import static android.provider.DeviceConfig.AttentionManagerService.COMPONENT_NAME;
 import static android.provider.DeviceConfig.AttentionManagerService.NAMESPACE;
-import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_COMPONENT_NAME;
-import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_SERVICE_ENABLED;
+import static android.provider.DeviceConfig.AttentionManagerService.SERVICE_ENABLED;
 
 import android.Manifest;
 import android.annotation.Nullable;
@@ -129,7 +129,7 @@
     }
 
     private boolean isServiceEnabled() {
-        final String enabled = DeviceConfig.getProperty(NAMESPACE, PROPERTY_SERVICE_ENABLED);
+        final String enabled = DeviceConfig.getProperty(NAMESPACE, SERVICE_ENABLED);
         return enabled == null ? DEFAULT_SERVICE_ENABLED : "true".equals(enabled);
     }
 
@@ -279,7 +279,7 @@
      * system.
      */
     private static ComponentName resolveAttentionService(Context context) {
-        final String flag = DeviceConfig.getProperty(NAMESPACE, PROPERTY_COMPONENT_NAME);
+        final String flag = DeviceConfig.getProperty(NAMESPACE, COMPONENT_NAME);
 
         final String componentNameString = flag != null ? flag : context.getString(
                 R.string.config_defaultAttentionService);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index d652f93..deaa931 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -600,7 +600,7 @@
                     break;
                 case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE:
                     mDeviceInventory.onSetA2dpSinkConnectionState(
-                            (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1, msg.arg2);
+                            (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
                     break;
                 case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
                     mDeviceInventory.onSetA2dpSourceConnectionState(
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index eb76e6e..97649a7 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -147,18 +147,20 @@
     }
 
     /*package*/ void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
-            @AudioService.BtProfileConnectionState int state, int a2dpVolume) {
+            @AudioService.BtProfileConnectionState int state) {
         final BluetoothDevice btDevice = btInfo.getBtDevice();
+        int a2dpVolume = btInfo.getVolume();
         if (AudioService.DEBUG_DEVICES) {
             Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
-                    + state + " is dock=" + btDevice.isBluetoothDock());
+                    + state + " is dock=" + btDevice.isBluetoothDock() + " vol=" + a2dpVolume);
         }
         String address = btDevice.getAddress();
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                "A2DP sink connected: device addr=" + address + " state=" + state));
+                "A2DP sink connected: device addr=" + address + " state=" + state
+                        + " vol=" + a2dpVolume));
 
         final int a2dpCodec;
         synchronized (mDeviceBroker.mA2dpAvrcpLock) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index df33bf2..9457fe3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -392,12 +392,14 @@
     * e.g. user on homescreen, no app playing anything, presses hardware volume buttons, this
     *    stream type is controlled.
     */
-   protected static final int DEFAULT_VOL_STREAM_NO_PLAYBACK = AudioSystem.STREAM_MUSIC;
+    protected static final int DEFAULT_VOL_STREAM_NO_PLAYBACK = AudioSystem.STREAM_MUSIC;
 
     private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
         public void onError(int error) {
             switch (error) {
             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
+                mRecordMonitor.clear();
+
                 sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
                         SENDMSG_NOOP, 0, 0, null, 0);
                 sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
@@ -906,6 +908,9 @@
         // Restore mono mode
         updateMasterMono(mContentResolver);
 
+        // Restore audio balance
+        updateMasterBalance(mContentResolver);
+
         // Restore ringer mode
         setRingerModeInt(getRingerModeInternal(), false);
 
@@ -1194,6 +1199,17 @@
         AudioSystem.setMasterMono(masterMono);
     }
 
+    private void updateMasterBalance(ContentResolver cr) {
+        final float masterBalance = System.getFloatForUser(
+                cr, System.MASTER_BALANCE, 0.f /* default */, UserHandle.USER_CURRENT);
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Master balance %f", masterBalance));
+        }
+        if (AudioSystem.setMasterBalance(masterBalance) != 0) {
+            Log.e(TAG, String.format("setMasterBalance failed for %f", masterBalance));
+        }
+    }
+
     private void sendEncodedSurroundMode(ContentResolver cr, String eventSource)
     {
         final int encodedSurroundMode = Settings.Global.getInt(
@@ -1370,6 +1386,8 @@
 
         updateMasterMono(cr);
 
+        updateMasterBalance(cr);
+
         // Each stream will read its own persisted settings
 
         // Broadcast the sticky intents
@@ -4946,6 +4964,8 @@
                 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
             mContentResolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.MASTER_MONO), false, this);
+            mContentResolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.MASTER_BALANCE), false, this);
 
             mEncodedSurroundMode = Settings.Global.getInt(
                     mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
@@ -4979,6 +4999,7 @@
                 }
                 readDockAudioSettings(mContentResolver);
                 updateMasterMono(mContentResolver);
+                updateMasterBalance(mContentResolver);
                 updateEncodedSurroundOutput();
                 sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
                 updateAssistantUId(false);
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 9d6628c..b2c7ff3 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -78,24 +78,27 @@
                 updateSnapshot(event, uid, session, source, recordingInfo,
                 portId, silenced, activeSource, clientEffects, effects);
         if (configsSystem != null){
-            synchronized (mClients) {
-                // list of recording configurations for "public consumption". It is only computed if
-                // there are non-system recording activity listeners.
-                final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients ?
-                        anonymizeForPublicConsumption(configsSystem) :
-                            new ArrayList<AudioRecordingConfiguration>();
-                final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
-                while (clientIterator.hasNext()) {
-                    final RecMonitorClient rmc = clientIterator.next();
-                    try {
-                        if (rmc.mIsPrivileged) {
-                            rmc.mDispatcherCb.dispatchRecordingConfigChange(configsSystem);
-                        } else {
-                            rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic);
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
+            dispatchCallbacks(configsSystem);
+        }
+    }
+    private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) {
+        synchronized (mClients) {
+            // list of recording configurations for "public consumption". It is only computed if
+            // there are non-system recording activity listeners.
+            final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients
+                    ? anonymizeForPublicConsumption(configs) :
+                      new ArrayList<AudioRecordingConfiguration>();
+            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+            while (clientIterator.hasNext()) {
+                final RecMonitorClient rmc = clientIterator.next();
+                try {
+                    if (rmc.mIsPrivileged) {
+                        rmc.mDispatcherCb.dispatchRecordingConfigChange(configs);
+                    } else {
+                        rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic);
                     }
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
                 }
             }
         }
@@ -130,6 +133,13 @@
         AudioSystem.setRecordingCallback(this);
     }
 
+    void clear() {
+        synchronized (mRecordConfigs) {
+            mRecordConfigs.clear();
+        }
+        dispatchCallbacks(new ArrayList<AudioRecordingConfiguration>());
+    }
+
     void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) {
         if (rcdb == null) {
             return;
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index eaa7a83..bd4acdb 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.security.KeyStore;
@@ -71,6 +72,11 @@
         stop(false /* initiatedByClient */);
     }
 
+    @Override
+    protected int statsAction() {
+        return BiometricsProtoEnums.ACTION_AUTHENTICATE;
+    }
+
     public boolean isBiometricPrompt() {
         return getCookie() != 0;
     }
@@ -80,8 +86,16 @@
     }
 
     @Override
+    protected boolean isCryptoOperation() {
+        return mOpId != 0;
+    }
+
+    @Override
     public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
+        super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(),
+                isBiometricPrompt());
+
         final BiometricServiceBase.ServiceListener listener = getListener();
 
         mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
@@ -142,10 +156,7 @@
                     final int errorCode = lockoutMode == LOCKOUT_TIMED
                             ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                             : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-                    if (listener != null) {
-                        listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
-                                getCookie());
-                    }
+                    onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
                 } else {
                     // Don't send onAuthenticationFailed if we're in lockout, it causes a
                     // janky UI on Keyguard/BiometricPrompt since "authentication failed"
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 15d66e6..b50b800 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -41,6 +41,7 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -64,6 +65,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBarService;
@@ -309,6 +311,7 @@
             // Continue authentication with the same modality/modalities after "try again" is
             // pressed
             final int mModality;
+            final boolean mRequireConfirmation;
 
             // The current state, which can be either idle, called, or started
             private int mState = STATE_AUTH_IDLE;
@@ -316,10 +319,13 @@
             // the authentication.
             byte[] mTokenEscrow;
 
+            // Timestamp when hardware authentication occurred
+            private long mAuthenticatedTimeMs;
+
             AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
                     int userId, IBiometricServiceReceiver receiver, String opPackageName,
                     Bundle bundle, int callingUid, int callingPid, int callingUserId,
-                    int modality) {
+                    int modality, boolean requireConfirmation) {
                 mModalitiesWaiting = modalities;
                 mToken = token;
                 mSessionId = sessionId;
@@ -331,6 +337,11 @@
                 mCallingPid = callingPid;
                 mCallingUserId = callingUserId;
                 mModality = modality;
+                mRequireConfirmation = requireConfirmation;
+            }
+
+            boolean isCrypto() {
+                return mSessionId != 0;
             }
 
             boolean containsCookie(int cookie) {
@@ -412,6 +423,7 @@
                         mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                         mCurrentAuthSession = null;
                     } else {
+                        mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
                         // Store the auth token and submit it to keystore after the confirmation
                         // button has been pressed.
                         mCurrentAuthSession.mTokenEscrow = token;
@@ -557,6 +569,8 @@
                     return;
                 }
 
+                logDialogDismissed(reason);
+
                 if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
                     // Positive button is used by passive modalities as a "confirm" button,
                     // do not send to client
@@ -599,6 +613,77 @@
                             mCurrentAuthSession.mModality);
                 });
             }
+
+            private void logDialogDismissed(int reason) {
+                if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+                    // Explicit auth, authentication confirmed.
+                    // Latency in this case is authenticated -> confirmed. <Biometric>Service
+                    // should have the first half (first acquired -> authenticated).
+                    final long latency = System.currentTimeMillis()
+                            - mCurrentAuthSession.mAuthenticatedTimeMs;
+
+                    if (LoggableMonitor.DEBUG) {
+                        Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
+                                + ", User: " + mCurrentAuthSession.mUserId
+                                + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
+                                + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+                                + ", RequireConfirmation: "
+                                    + mCurrentAuthSession.mRequireConfirmation
+                                + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
+                                + ", Latency: " + latency);
+                    }
+
+                    StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
+                            statsModality(),
+                            mCurrentAuthSession.mUserId,
+                            mCurrentAuthSession.isCrypto(),
+                            BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+                            mCurrentAuthSession.mRequireConfirmation,
+                            StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
+                            latency);
+                } else {
+                    int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
+                            ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
+                            : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
+                                    ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
+                                    : 0;
+                    if (LoggableMonitor.DEBUG) {
+                        Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
+                                + ", User: " + mCurrentAuthSession.mUserId
+                                + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
+                                + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
+                                + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
+                                + ", Error: " + error);
+                    }
+                    // Auth canceled
+                    StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
+                            statsModality(),
+                            mCurrentAuthSession.mUserId,
+                            mCurrentAuthSession.isCrypto(),
+                            BiometricsProtoEnums.ACTION_AUTHENTICATE,
+                            BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+                            error,
+                            0 /* vendorCode */);
+                }
+            }
+
+            private int statsModality() {
+                int modality = 0;
+                if (mCurrentAuthSession == null) {
+                    return BiometricsProtoEnums.MODALITY_UNKNOWN;
+                }
+                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
+                        != 0) {
+                    modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
+                }
+                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
+                    modality |= BiometricsProtoEnums.MODALITY_IRIS;
+                }
+                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
+                    modality |= BiometricsProtoEnums.MODALITY_FACE;
+                }
+                return modality;
+            }
         };
 
         @Override // Binder call
@@ -719,14 +804,17 @@
             // result back to the client.
             // TODO(b/123378871): Remove when moved.
             if (bundle.getBoolean(BiometricPrompt.KEY_ENABLE_FALLBACK)) {
-                mConfirmDeviceCredentialReceiver = receiver;
-                final KeyguardManager kgm = getContext().getSystemService(KeyguardManager.class);
-                // Use this so we don't need to duplicate logic..
-                final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
-                        null /* description */);
-                // Then give it the bundle to do magic behavior..
-                intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
-                getContext().startActivityAsUser(intent, UserHandle.CURRENT);
+                mHandler.post(() -> {
+                    mConfirmDeviceCredentialReceiver = receiver;
+                    final KeyguardManager kgm = getContext().getSystemService(
+                            KeyguardManager.class);
+                    // Use this so we don't need to duplicate logic..
+                    final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
+                            null /* description */);
+                    // Then give it the bundle to do magic behavior..
+                    intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
+                    getContext().startActivityAsUser(intent, UserHandle.CURRENT);
+                });
                 return;
             }
 
@@ -815,7 +903,11 @@
             try {
                 boolean requireConfirmation = bundle.getBoolean(
                         BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
-
+                if ((modality & TYPE_FACE) != 0) {
+                    // Check if the user has forced confirmation to be required in Settings.
+                    requireConfirmation = requireConfirmation
+                            || mSettingObserver.getFaceAlwaysRequireConfirmation();
+                }
                 // Generate random cookies to pass to the services that should prepare to start
                 // authenticating. Store the cookie here and wait for all services to "ack"
                 // with the cookie. Once all cookies are received, we can show the prompt
@@ -827,7 +919,7 @@
                 authenticators.put(modality, cookie);
                 mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
                         receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
-                        modality);
+                        modality, requireConfirmation);
                 mPendingAuthSession.mState = STATE_AUTH_CALLED;
                 // No polymorphism :(
                 if ((modality & TYPE_FINGERPRINT) != 0) {
@@ -839,9 +931,6 @@
                     Slog.w(TAG, "Iris unsupported");
                 }
                 if ((modality & TYPE_FACE) != 0) {
-                    // Check if the user has forced confirmation to be required in Settings.
-                    requireConfirmation = requireConfirmation
-                            || mSettingObserver.getFaceAlwaysRequireConfirmation();
                     mFaceService.prepareForAuthentication(requireConfirmation,
                             token, sessionId, userId, mInternalReceiver, opPackageName,
                             cookie, callingUid, callingPid, callingUserId);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 2791165..b65535a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -35,6 +35,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -55,6 +56,7 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
@@ -220,8 +222,16 @@
      */
     protected void notifyClientActiveCallbacks(boolean isActive) {}
 
+    protected abstract int statsModality();
+
     protected abstract class AuthenticationClientImpl extends AuthenticationClient {
 
+        // Used to check if the public API that was invoked was from FingerprintManager. Only
+        // to be overridden by FingerprintService.
+        protected boolean isFingerprint() {
+            return false;
+        }
+
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
                 boolean restricted, String owner, int cookie, boolean requireConfirmation) {
@@ -230,6 +240,19 @@
         }
 
         @Override
+        protected int statsClient() {
+            if (isKeyguard(getOwnerString())) {
+                return BiometricsProtoEnums.CLIENT_KEYGUARD;
+            } else if (isBiometricPrompt()) {
+                return BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
+            } else if (isFingerprint()) {
+                return BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
+            } else {
+                return BiometricsProtoEnums.CLIENT_UNKNOWN;
+            }
+        }
+
+        @Override
         public void onStart() {
             try {
                 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
@@ -296,7 +319,7 @@
         }
     }
 
-    protected class RemovalClientImpl extends RemovalClient {
+    protected abstract class RemovalClientImpl extends RemovalClient {
         private boolean mShouldNotify;
 
         public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
@@ -318,7 +341,7 @@
         }
     }
 
-    protected class EnumerateClientImpl extends EnumerateClient {
+    protected abstract class EnumerateClientImpl extends EnumerateClient {
 
         public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int groupId, int userId,
@@ -600,6 +623,8 @@
         mHALDeathCount++;
         handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                 0 /*vendorCode */);
+
+        StatsLog.write(StatsLog.BIOMETRIC_HAL_DEATH_REPORTED, statsModality());
     }
 
     protected ClientMonitor getCurrentClient() {
@@ -653,7 +678,6 @@
             } else {
                 updateActiveGroup(mCurrentUserId, null);
             }
-
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index d19aff6..e80b39b 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -36,7 +36,7 @@
  * the current client.  Subclasses are responsible for coordinating the interaction with
  * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
  */
-public abstract class ClientMonitor implements IBinder.DeathRecipient {
+public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {
     protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
     protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
     private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
@@ -157,6 +157,7 @@
      * @return true if client should be removed
      */
     public boolean onAcquired(int acquiredInfo, int vendorCode) {
+        super.logOnAcquired(acquiredInfo, vendorCode, getTargetUserId());
         try {
             if (mListener != null) {
                 mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
@@ -180,6 +181,7 @@
      * @return true if client should be removed
      */
     public boolean onError(long deviceId, int error, int vendorCode) {
+        super.logOnError(error, vendorCode, getTargetUserId());
         try {
             if (mListener != null) {
                 mListener.onError(deviceId, error, vendorCode, getCookie());
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 3ff94bc..1124b7f 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -51,6 +52,11 @@
     }
 
     @Override
+    protected int statsAction() {
+        return BiometricsProtoEnums.ACTION_ENROLL;
+    }
+
+    @Override
     public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         if (remaining == 0) {
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index df6220c..0f57f48 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -38,6 +39,11 @@
     }
 
     @Override
+    protected int statsAction() {
+        return BiometricsProtoEnums.ACTION_ENUMERATE;
+    }
+
+    @Override
     public int start() {
         // The biometric template ids will be removed when we get confirmation from the HAL
         try {
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
new file mode 100644
index 0000000..91c924d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.biometrics;
+
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.face.FaceManager;
+import android.util.Slog;
+import android.util.StatsLog;
+
+/**
+ * Abstract class that adds logging functionality to the ClientMonitor classes.
+ */
+public abstract class LoggableMonitor {
+
+    public static final String TAG = "BiometricStats";
+    public static final boolean DEBUG = true;
+
+    private long mFirstAcquireTimeMs;
+
+    /**
+     * Only valid for AuthenticationClient.
+     * @return true if the client is authenticating for a crypto operation.
+     */
+    protected boolean isCryptoOperation() {
+        return false;
+    }
+
+    /**
+     * @return One of {@link BiometricsProtoEnums} MODALITY_* constants.
+     */
+    protected abstract int statsModality();
+
+    /**
+     * Action == enroll, authenticate, remove, enumerate.
+     * @return One of {@link BiometricsProtoEnums} ACTION_* constants.
+     */
+    protected abstract int statsAction();
+
+    /**
+     * Only matters for AuthenticationClient. Should only be overridden in
+     * {@link BiometricServiceBase}, which determines if a client is for BiometricPrompt, Keyguard,
+     * etc.
+     * @return one of {@link BiometricsProtoEnums} CLIENT_* constants.
+     */
+    protected int statsClient() {
+        return BiometricsProtoEnums.CLIENT_UNKNOWN;
+    }
+
+    protected final void logOnAcquired(int acquiredInfo, int vendorCode, int targetUserId) {
+        if (statsModality() == BiometricsProtoEnums.MODALITY_FACE) {
+            if (acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+                mFirstAcquireTimeMs = System.currentTimeMillis();
+            }
+        } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+            if (mFirstAcquireTimeMs == 0) {
+                mFirstAcquireTimeMs = System.currentTimeMillis();
+            }
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Acquired! Modality: " + statsModality()
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCryptoOperation()
+                    + ", Action: " + statsAction()
+                    + ", Client: " + statsClient()
+                    + ", AcquiredInfo: " + acquiredInfo
+                    + ", VendorCode: " + vendorCode);
+        }
+        StatsLog.write(StatsLog.BIOMETRIC_ACQUIRED,
+                statsModality(),
+                targetUserId,
+                isCryptoOperation(),
+                statsAction(),
+                statsClient(),
+                acquiredInfo,
+                0 /* vendorCode */); // Don't log vendorCode for now
+    }
+
+    protected final void logOnError(int error, int vendorCode, int targetUserId) {
+        if (DEBUG) {
+            Slog.v(TAG, "Error! Modality: " + statsModality()
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCryptoOperation()
+                    + ", Action: " + statsAction()
+                    + ", Client: " + statsClient()
+                    + ", Error: " + error
+                    + ", VendorCode: " + vendorCode);
+        }
+        StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
+                statsModality(),
+                targetUserId,
+                isCryptoOperation(),
+                statsAction(),
+                statsClient(),
+                error,
+                vendorCode);
+    }
+
+    protected final void logOnAuthenticated(boolean authenticated, boolean requireConfirmation,
+            int targetUserId, boolean isBiometricPrompt) {
+        int authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
+        if (!authenticated) {
+            authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
+        } else {
+            // Authenticated
+            if (isBiometricPrompt && requireConfirmation) {
+                authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
+            } else {
+                authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
+            }
+        }
+
+        // Only valid if we have a first acquired time, otherwise set to -1
+        final long latency = mFirstAcquireTimeMs != 0
+                ? (System.currentTimeMillis() - mFirstAcquireTimeMs)
+                : -1;
+
+        if (DEBUG) {
+            Slog.v(TAG, "Authenticated! Modality: " + statsModality()
+                    + ", User: " + targetUserId
+                    + ", IsCrypto: " + isCryptoOperation()
+                    + ", Client: " + statsClient()
+                    + ", RequireConfirmation: " + requireConfirmation
+                    + ", State: " + authState
+                    + ", Latency: " + latency);
+        }
+
+        StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
+                statsModality(),
+                targetUserId,
+                isCryptoOperation(),
+                statsClient(),
+                requireConfirmation,
+                authState,
+                latency);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index be233ec..0509067 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -43,6 +44,11 @@
     }
 
     @Override
+    protected int statsAction() {
+        return BiometricsProtoEnums.ACTION_REMOVE;
+    }
+
+    @Override
     public int start() {
         // The biometric template ids will be removed when we get confirmation from the HAL
         try {
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index d4be539..90342ee 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -27,6 +27,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
@@ -90,6 +91,11 @@
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
                     restricted, owner, cookie, requireConfirmation);
         }
+
+        @Override
+        protected int statsModality() {
+            return FaceService.this.statsModality();
+        }
     }
 
     /**
@@ -126,6 +132,11 @@
                 public boolean shouldVibrate() {
                     return false;
                 }
+
+                @Override
+                protected int statsModality() {
+                    return FaceService.this.statsModality();
+                }
             };
 
             enrollInternal(client, UserHandle.getCallingUserId());
@@ -206,7 +217,12 @@
             final boolean restricted = isRestricted();
             final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
-                    userId, restricted, token.toString());
+                    userId, restricted, token.toString()) {
+                @Override
+                protected int statsModality() {
+                    return FaceService.this.statsModality();
+                }
+            };
             client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
@@ -219,7 +235,12 @@
             final boolean restricted = isRestricted();
             final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName());
+                    restricted, getContext().getOpPackageName()) {
+                @Override
+                protected int statsModality() {
+                    return FaceService.this.statsModality();
+                }
+            };
             enumerateInternal(client);
         }
 
@@ -770,6 +791,11 @@
         // noop for Face.
     }
 
+    @Override
+    protected int statsModality() {
+        return BiometricsProtoEnums.MODALITY_FACE;
+    }
+
     /** Gets the face daemon */
     private synchronized IBiometricsFace getFaceDaemon() {
         if (mDaemon == null) {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index f84cda03..62947c7 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -30,6 +30,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -101,6 +102,11 @@
     }
 
     private final class FingerprintAuthClient extends AuthenticationClientImpl {
+        @Override
+        protected boolean isFingerprint() {
+            return true;
+        }
+
         public FingerprintAuthClient(Context context,
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -109,6 +115,11 @@
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
                     restricted, owner, cookie, requireConfirmation);
         }
+
+        @Override
+        protected int statsModality() {
+            return FingerprintService.this.statsModality();
+        }
     }
 
     /**
@@ -147,6 +158,11 @@
                 public boolean shouldVibrate() {
                     return true;
                 }
+
+                @Override
+                protected int statsModality() {
+                    return FingerprintService.this.statsModality();
+                }
             };
 
             enrollInternal(client, userId);
@@ -225,7 +241,12 @@
             final boolean restricted = isRestricted();
             final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
-                    userId, restricted, token.toString());
+                    userId, restricted, token.toString()) {
+                @Override
+                protected int statsModality() {
+                    return FingerprintService.this.statsModality();
+                }
+            };
             client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
@@ -238,7 +259,12 @@
             final boolean restricted = isRestricted();
             final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName());
+                    restricted, getContext().getOpPackageName()) {
+                @Override
+                protected int statsModality() {
+                    return FingerprintService.this.statsModality();
+                }
+            };
             enumerateInternal(client);
         }
 
@@ -544,6 +570,11 @@
             }
             return remaining == 0;
         }
+
+        @Override
+        protected int statsModality() {
+            return FingerprintService.this.statsModality();
+        }
     }
 
     /**
@@ -558,6 +589,11 @@
                     restricted,
                     owner);
         }
+
+        @Override
+        protected int statsModality() {
+            return FingerprintService.this.statsModality();
+        }
     }
 
     private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
@@ -885,6 +921,11 @@
         }
     }
 
+    @Override
+    protected int statsModality() {
+        return BiometricsProtoEnums.MODALITY_FINGERPRINT;
+    }
+
     /** Gets the fingerprint daemon */
     private synchronized IBiometricsFingerprint getFingerprintDaemon() {
         if (mDaemon == null) {
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index 37cdc2a..eb457b6 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.iris;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
@@ -128,4 +129,9 @@
     protected boolean checkAppOps(int uid, String opPackageName) {
         return false;
     }
+
+    @Override
+    protected int statsModality() {
+        return BiometricsProtoEnums.MODALITY_IRIS;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index eb5be77..a14fd17 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -121,7 +121,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-
 /**
  * @hide
  *
@@ -223,7 +222,8 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
-        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
+        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM,
+                mLog, systemProperties);
         mCarrierConfigChange = new VersionedBroadcastListener(
                 "CarrierConfigChangeListener", mContext, smHandler, filter,
                 (Intent ignored) -> {
@@ -470,6 +470,7 @@
                 } else {
                     sendTetherResult(receiver, resultCode);
                 }
+                mEntitlementMgr.updateEntitlementCacheValue(type, resultCode);
             }
         };
 
@@ -1662,6 +1663,14 @@
         mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
     }
 
+    /** Get the latest value of the tethering entitlement check. */
+    public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+            boolean showEntitlementUi) {
+        if (receiver != null) {
+            mEntitlementMgr.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         // Binder.java closes the resource for us.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7e95f10..e1af81b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -166,6 +166,7 @@
     private final NetworkInfo mNetworkInfo;
     private String mPackage;
     private int mOwnerUID;
+    private boolean mIsPackageTargetingAtLeastQ;
     private String mInterface;
     private Connection mConnection;
     private LegacyVpnRunner mLegacyVpnRunner;
@@ -227,6 +228,7 @@
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserHandle);
+        mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
 
         try {
             netService.registerObserver(mObserver);
@@ -268,8 +270,11 @@
 
     public void updateCapabilities() {
         final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
+        // Only apps targeting Q and above can explicitly declare themselves as metered.
+        final boolean isAlwaysMetered =
+                mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered);
         updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
-                mNetworkCapabilities);
+                mNetworkCapabilities, isAlwaysMetered);
 
         if (mNetworkAgent != null) {
             mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
@@ -278,11 +283,13 @@
 
     @VisibleForTesting
     public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
-            NetworkCapabilities caps) {
+            NetworkCapabilities caps, boolean isAlwaysMetered) {
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        boolean metered = false;
+        // VPN's meteredness is OR'd with isAlwaysMetered and meteredness of its underlying
+        // networks.
+        boolean metered = isAlwaysMetered;
         boolean roaming = false;
         boolean congested = false;
 
@@ -725,6 +732,7 @@
             Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
             mPackage = newPackage;
             mOwnerUID = getAppUid(newPackage, mUserHandle);
+            mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
             try {
                 mNetd.allowProtect(mOwnerUID);
             } catch (Exception e) {
@@ -790,6 +798,21 @@
         return result;
     }
 
+    private boolean doesPackageTargetAtLeastQ(String packageName) {
+        if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+            return true;
+        }
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            ApplicationInfo appInfo =
+                    pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+            return appInfo.targetSdkVersion >= VERSION_CODES.Q;
+        } catch (NameNotFoundException unused) {
+            Log.w(TAG, "Can't find \"" + packageName + "\"");
+            return false;
+        }
+    }
+
     public NetworkInfo getNetworkInfo() {
         return mNetworkInfo;
     }
@@ -1078,6 +1101,8 @@
                 // as rules are deleted. This prevents data leakage as the rules are moved over.
                 agentDisconnect(oldNetworkAgent);
             }
+            // Set up VPN's capabilities such as meteredness.
+            updateCapabilities();
 
             if (oldConnection != null) {
                 mContext.unbindService(oldConnection);
@@ -1778,6 +1803,7 @@
         config.user = profile.key;
         config.interfaze = iface;
         config.session = profile.name;
+        config.isMetered = false;
 
         config.addLegacyRoutes(profile.routes);
         if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
index a4e3e1d..75aac10 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -21,6 +21,9 @@
 import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
 import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
 import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
 
 import static com.android.internal.R.string.config_wifi_tether_enable;
 
@@ -31,15 +34,21 @@
 import android.content.res.Resources;
 import android.net.util.SharedLog;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.StateMachine;
 import com.android.server.connectivity.MockableSystemProperties;
 
 /**
@@ -50,6 +59,7 @@
  */
 public class EntitlementManager {
     private static final String TAG = EntitlementManager.class.getSimpleName();
+    private static final boolean DBG = false;
 
     // {@link ComponentName} of the Service used to run tether provisioning.
     private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
@@ -65,15 +75,19 @@
     private final Context mContext;
     private final MockableSystemProperties mSystemProperties;
     private final SharedLog mLog;
+    private final Handler mMasterHandler;
+    private final SparseIntArray mEntitlementCacheValue;
     @Nullable
     private TetheringConfiguration mConfig;
 
-    public EntitlementManager(Context ctx, SharedLog log,
+    public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
             MockableSystemProperties systemProperties) {
         mContext = ctx;
-        mLog = log;
+        mLog = log.forSubComponent(TAG);
         mCurrentTethers = new ArraySet<Integer>();
         mSystemProperties = systemProperties;
+        mEntitlementCacheValue = new SparseIntArray();
+        mMasterHandler = tetherMasterSM.getHandler();
     }
 
     /**
@@ -128,6 +142,10 @@
      * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
      */
     public void reevaluateSimCardProvisioning() {
+        synchronized (mEntitlementCacheValue) {
+            mEntitlementCacheValue.clear();
+        }
+
         if (!mConfig.hasMobileHotspotProvisionApp()) return;
         if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
 
@@ -175,6 +193,11 @@
     }
 
     public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        runUiTetherProvisioning(type, receiver);
+    }
+
+    @VisibleForTesting
+    protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
         Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
@@ -221,4 +244,70 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
+
+    private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
+        ResultReceiver rr = new ResultReceiver(mMasterHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
+                receiver.send(updatedCacheValue, null);
+            }
+        };
+
+        return writeToParcel(rr);
+    }
+
+    private ResultReceiver writeToParcel(final ResultReceiver receiver) {
+        // This is necessary to avoid unmarshalling issues when sending the receiver
+        // across processes.
+        Parcel parcel = Parcel.obtain();
+        receiver.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return receiverForSending;
+    }
+
+    /**
+     * Update the last entitlement value to internal cache
+     *
+     * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+     * @param resultCode last entitlement value
+     * @return the last updated entitlement value
+     */
+    public int updateEntitlementCacheValue(int type, int resultCode) {
+        if (DBG) {
+            Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
+        }
+        synchronized (mEntitlementCacheValue) {
+            if (resultCode == TETHER_ERROR_NO_ERROR) {
+                mEntitlementCacheValue.put(type, resultCode);
+                return resultCode;
+            } else {
+                mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
+                return TETHER_ERROR_PROVISION_FAILED;
+            }
+        }
+    }
+
+    /** Get the last value of the tethering entitlement check. */
+    public void getLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
+            boolean showEntitlementUi) {
+        if (!isTetherProvisioningRequired()) {
+            receiver.send(TETHER_ERROR_NO_ERROR, null);
+            return;
+        }
+
+        final int cacheValue;
+        synchronized (mEntitlementCacheValue) {
+            cacheValue = mEntitlementCacheValue.get(
+                downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
+        }
+        if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
+            receiver.send(cacheValue, null);
+        } else {
+            ResultReceiver proxy = buildProxyReceiver(downstream, receiver);
+            runUiTetherProvisioning(downstream, proxy);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index a42efe9..6d6f81e 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -81,8 +81,8 @@
     /**
      * Get a reference to the EntitlementManager to be used by tethering.
      */
-    public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
-            MockableSystemProperties systemProperties) {
-        return new EntitlementManager(ctx, log, systemProperties);
+    public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
+            SharedLog log, MockableSystemProperties systemProperties) {
+        return new EntitlementManager(ctx, target, log, systemProperties);
     }
 }
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 019d726..727cf0e 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -27,12 +27,17 @@
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
 import android.database.ContentObserver;
+import android.graphics.PixelFormat;
 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.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Environment;
@@ -48,6 +53,7 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
+import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +61,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.RingBuffer;
+import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
 
@@ -111,6 +118,8 @@
     private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
     private static final String ATTR_POWER_SAVE = "powerSaveFactor";
     private static final String ATTR_USER_POINT = "userPoint";
+    private static final String ATTR_COLOR_SAMPLE_DURATION = "colorSampleDuration";
+    private static final String ATTR_COLOR_VALUE_BUCKETS = "colorValueBuckets";
 
     private static final int MSG_BACKGROUND_START = 0;
     private static final int MSG_BRIGHTNESS_CHANGED = 1;
@@ -119,6 +128,10 @@
 
     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
+    private static final long COLOR_SAMPLE_DURATION = TimeUnit.SECONDS.toSeconds(10);
+    // Sample chanel 2 of HSV which is the Value component.
+    private static final int COLOR_SAMPLE_COMPONENT_MASK = 0x1 << 2;
+
     // Lock held while accessing mEvents, is held while writing events to flash.
     private final Object mEventsLock = new Object();
     @GuardedBy("mEventsLock")
@@ -136,12 +149,16 @@
     private final ContentResolver mContentResolver;
     private final Handler mBgHandler;
 
-    // mBroadcastReceiver,  mSensorListener, mSettingsObserver and mSensorRegistered
-    // should only be used on the mBgHandler thread.
+    // These members should only be accessed on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
     private SettingsObserver mSettingsObserver;
+    private DisplayListener mDisplayListener;
     private boolean mSensorRegistered;
+    private boolean mColorSamplingEnabled;
+    private int mNoFramesToSample;
+    private float mFrameRate;
+    // End of block of members that should only be accessed on the mBgHandler thread.
 
     private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
 
@@ -208,6 +225,7 @@
             mLastBrightness = initialBrightness;
             mStarted = true;
         }
+        enableColorSampling();
     }
 
     /** Stop listening for events */
@@ -226,6 +244,7 @@
         synchronized (mDataCollectionLock) {
             mStarted = false;
         }
+        disableColorSampling();
     }
 
     public void onSwitchUser(@UserIdInt int newUserId) {
@@ -367,6 +386,17 @@
         builder.setColorTemperature(mInjector.getColorTemperature(mContext,
                 UserHandle.USER_CURRENT));
 
+        if (mColorSamplingEnabled) {
+            DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
+            if (sample != null && sample.getSampleComponent(
+                    DisplayedContentSample.ColorComponent.CHANNEL2) != null) {
+                float numMillis = (sample.getNumFrames() / mFrameRate) * 1000.0f;
+                builder.setColorValues(
+                        sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2),
+                        Math.round(numMillis));
+            }
+        }
+
         BrightnessChangeEvent event = builder.build();
         if (DEBUG) {
             Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
@@ -541,6 +571,19 @@
                 }
                 out.attribute(null, ATTR_LUX, luxValues.toString());
                 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
+                if (toWrite[i].colorValueBuckets != null
+                        && toWrite[i].colorValueBuckets.length > 0) {
+                    out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
+                            Long.toString(toWrite[i].colorSampleDuration));
+                    StringBuilder buckets = new StringBuilder();
+                    for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
+                        if (j > 0) {
+                            buckets.append(',');
+                        }
+                        buckets.append(Long.toString(toWrite[i].colorValueBuckets[j]));
+                    }
+                    out.attribute(null, ATTR_COLOR_VALUE_BUCKETS, buckets.toString());
+                }
                 out.endTag(null, TAG_EVENT);
             }
         }
@@ -628,6 +671,20 @@
                         builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
                     }
 
+                    String colorSampleDurationString =
+                            parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
+                    String colorValueBucketsString =
+                            parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS);
+                    if (colorSampleDurationString != null && colorValueBucketsString != null) {
+                        long colorSampleDuration = Long.parseLong(colorSampleDurationString);
+                        String[] buckets = colorValueBucketsString.split(",");
+                        long[] bucketValues = new long[buckets.length];
+                        for (int i = 0; i < bucketValues.length; ++i) {
+                            bucketValues[i] = Long.parseLong(buckets[i]);
+                        }
+                        builder.setColorValues(bucketValues, colorSampleDuration);
+                    }
+
                     BrightnessChangeEvent event = builder.build();
                     if (DEBUG) {
                         Slog.i(TAG, "Read event " + event.brightness
@@ -695,6 +752,73 @@
 
     private void dumpLocal(PrintWriter pw) {
         pw.println("  mSensorRegistered=" + mSensorRegistered);
+        pw.println("  mColorSamplingEnabled=" + mColorSamplingEnabled);
+        pw.println("  mNoFramesToSample=" + mNoFramesToSample);
+        pw.println("  mFrameRate=" + mFrameRate);
+    }
+
+    private void enableColorSampling() {
+        if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
+                || !mInjector.isInteractive(mContext)
+                || mColorSamplingEnabled) {
+            return;
+        }
+
+        mFrameRate = mInjector.getFrameRate(mContext);
+        if (mFrameRate <= 0) {
+            Slog.wtf(TAG, "Default display has a zero or negative framerate.");
+            return;
+        }
+        mNoFramesToSample = (int) (mFrameRate * COLOR_SAMPLE_DURATION);
+
+        DisplayedContentSamplingAttributes attributes = mInjector.getSamplingAttributes();
+        if (DEBUG && attributes != null) {
+            Slog.d(TAG, "Color sampling"
+                    + " mask=0x" + Integer.toHexString(attributes.getComponentMask())
+                    + " dataSpace=0x" + Integer.toHexString(attributes.getDataspace())
+                    + " pixelFormat=0x" + Integer.toHexString(attributes.getPixelFormat()));
+        }
+        // Do we support sampling the Value component of HSV
+        if (attributes != null && attributes.getPixelFormat() == PixelFormat.HSV_888
+                && (attributes.getComponentMask() & COLOR_SAMPLE_COMPONENT_MASK) != 0) {
+
+            mColorSamplingEnabled = mInjector.enableColorSampling(/* enable= */true,
+                    mNoFramesToSample);
+            if (DEBUG) {
+                Slog.i(TAG, "turning on color sampling for "
+                        + mNoFramesToSample + " frames, success=" + mColorSamplingEnabled);
+            }
+        }
+        if (mColorSamplingEnabled && mDisplayListener == null) {
+            mDisplayListener = new DisplayListener();
+            mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
+        }
+    }
+
+    private void disableColorSampling() {
+        if (!mColorSamplingEnabled) {
+            return;
+        }
+        mInjector.enableColorSampling(/* enable= */ false, /* noFrames= */ 0);
+        mColorSamplingEnabled = false;
+        if (mDisplayListener != null) {
+            mInjector.unRegisterDisplayListener(mContext, mDisplayListener);
+            mDisplayListener = null;
+        }
+        if (DEBUG) {
+            Slog.i(TAG, "turning off color sampling");
+        }
+    }
+
+    private void updateColorSampling() {
+        if (!mColorSamplingEnabled) {
+            return;
+        }
+        float frameRate = mInjector.getFrameRate(mContext);
+        if (frameRate != mFrameRate) {
+            disableColorSampling();
+            enableColorSampling();
+        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
@@ -768,6 +892,26 @@
         }
     }
 
+    private final class DisplayListener implements DisplayManager.DisplayListener {
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            // Ignore
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            // Ignore
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                updateColorSampling();
+            }
+        }
+    }
+
     private final class SettingsObserver extends ContentObserver {
         public SettingsObserver(Handler handler) {
             super(handler);
@@ -828,9 +972,11 @@
                     break;
                 case MSG_START_SENSOR_LISTENER:
                     startSensorListener();
+                    enableColorSampling();
                     break;
                 case MSG_STOP_SENSOR_LISTENER:
                     stopSensorListener();
+                    disableColorSampling();
                     break;
             }
         }
@@ -957,5 +1103,44 @@
         public boolean isNightModeActive(Context context, int userId) {
             return new ColorDisplayController(context, userId).isActivated();
         }
+
+        public DisplayedContentSample sampleColor(int noFramesToSample) {
+            final DisplayManagerInternal displayManagerInternal =
+                    LocalServices.getService(DisplayManagerInternal.class);
+            return displayManagerInternal.getDisplayedContentSample(
+                   Display.DEFAULT_DISPLAY, noFramesToSample, 0);
+        }
+
+        public float getFrameRate(Context context) {
+            final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            return display.getRefreshRate();
+        }
+
+        public DisplayedContentSamplingAttributes getSamplingAttributes() {
+            final DisplayManagerInternal displayManagerInternal =
+                    LocalServices.getService(DisplayManagerInternal.class);
+            return displayManagerInternal.getDisplayedContentSamplingAttributes(
+                    Display.DEFAULT_DISPLAY);
+        }
+
+        public boolean enableColorSampling(boolean enable, int noFrames) {
+            final DisplayManagerInternal displayManagerInternal =
+                    LocalServices.getService(DisplayManagerInternal.class);
+            return displayManagerInternal.setDisplayedContentSamplingEnabled(
+                    Display.DEFAULT_DISPLAY, enable, COLOR_SAMPLE_COMPONENT_MASK, noFrames);
+        }
+
+        public void registerDisplayListener(Context context,
+                DisplayManager.DisplayListener listener, Handler handler) {
+            final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+            displayManager.registerDisplayListener(listener, handler);
+        }
+
+        public void unRegisterDisplayListener(Context context,
+                DisplayManager.DisplayListener listener) {
+            final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+            displayManager.unregisterDisplayListener(listener);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index eb0ed0a..591ce8d 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -55,6 +55,7 @@
 import android.opengl.Matrix;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemProperties;
@@ -66,6 +67,9 @@
 import android.view.SurfaceControl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.DisplayPrimaries;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
@@ -145,43 +149,20 @@
         @Override
         public void setUp(Context context, boolean needsLinear) {
             mSetUp = false;
+            final Resources res = context.getResources();
 
-            final Resources res = getContext().getResources();
-            final String[] displayPrimariesValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
+            if (displayColorSpaceRGB == null) {
+                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
+                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
+                if (displayColorSpaceRGB == null) {
+                    Slog.e(TAG, "Failed to get display color space from resources");
+                    return;
+                }
+            }
+
             final String[] nominalWhiteValues = res.getStringArray(
                     R.array.config_displayWhiteBalanceDisplayNominalWhite);
-
-            if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
-                Slog.e(TAG, "Unexpected display white balance primaries resource length " +
-                        displayPrimariesValues.length);
-                return;
-            }
-
-            if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
-                Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
-                        nominalWhiteValues.length);
-                return;
-            }
-
-            float[] displayRedGreenBlueXYZ =
-                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
-            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
-                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
-            }
-            for (int i = 0; i < displayWhiteXYZ.length; i++) {
-                displayWhiteXYZ[i] = Float.parseFloat(
-                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
-            }
-
-            final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
-                    "Display Color Space",
-                    displayRedGreenBlueXYZ,
-                    displayWhiteXYZ,
-                    2.2f // gamma, unused for display white balance
-            );
-
             float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
             for (int i = 0; i < nominalWhiteValues.length; i++) {
                 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
@@ -190,14 +171,14 @@
             final int colorTemperatureMin = res.getInteger(
                     R.integer.config_displayWhiteBalanceColorTemperatureMin);
             if (colorTemperatureMin <= 0) {
-                Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
+                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
                 return;
             }
 
             final int colorTemperatureMax = res.getInteger(
                     R.integer.config_displayWhiteBalanceColorTemperatureMax);
             if (colorTemperatureMax < colorTemperatureMin) {
-                Slog.e(TAG, "display white balance max temp must be greater or equal to min");
+                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
                 return;
             }
 
@@ -323,6 +304,57 @@
                         matrixToString(mMatrixDisplayWhiteBalance, 4));
             }
         }
+
+        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
+            return new ColorSpace.Rgb(
+                "Display Color Space",
+                redGreenBlueXYZ,
+                whiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
+            IBinder displayToken = SurfaceControl.getBuiltInDisplay(
+                    SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+            if (displayToken == null) {
+                return null;
+            }
+
+            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+            if (primaries == null || primaries.red == null || primaries.green == null ||
+                primaries.blue == null || primaries.white == null) {
+                return null;
+            }
+
+            return makeRgbColorSpaceFromXYZ(
+                    new float[] {
+                        primaries.red.X, primaries.red.Y, primaries.red.Z,
+                        primaries.green.X, primaries.green.Y, primaries.green.Z,
+                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
+                    },
+                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
+                    );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            float[] displayRedGreenBlueXYZ =
+                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
+        }
     };
 
     private final TintController mGlobalSaturationTintController = new TintController() {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 0400b56..f2c539c 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -575,7 +575,7 @@
                 final SurfaceControl.Builder builder =
                         new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
                 if (mMode == MODE_FADE) {
-                    builder.setColorLayer();
+                    builder.setColorLayer(true);
                 } else {
                     builder.setBufferSize(mDisplayWidth, mDisplayHeight);
                 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c9df86e..80ea1da 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2196,6 +2196,22 @@
             }
         }
 
+        void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+            if (mDisplayPowerController != null) {
+                synchronized (mSyncRoot) {
+                    mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
+                }
+            }
+        }
+
+        void setAmbientColorTemperatureOverride(float cct) {
+            if (mDisplayPowerController != null) {
+                synchronized (mSyncRoot) {
+                    mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
+                }
+            }
+        }
+
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index abbfc7b..04d28ea 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -45,6 +45,12 @@
                 return setAutoBrightnessLoggingEnabled(true);
             case "ab-logging-disable":
                 return setAutoBrightnessLoggingEnabled(false);
+            case "dwb-logging-enable":
+                return setDisplayWhiteBalanceLoggingEnabled(true);
+            case "dwb-logging-disable":
+                return setDisplayWhiteBalanceLoggingEnabled(false);
+            case "dwb-set-cct":
+                return setAmbientColorTemperatureOverride();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -65,6 +71,12 @@
         pw.println("    Enable auto-brightness logging.");
         pw.println("  ab-logging-disable");
         pw.println("    Disable auto-brightness logging.");
+        pw.println("  dwb-logging-enable");
+        pw.println("    Enable display white-balance logging.");
+        pw.println("  dwb-logging-disable");
+        pw.println("    Disable display white-balance logging.");
+        pw.println("  dwb-set-cct CCT");
+        pw.println("    Sets the ambient color temperature override to CCT (use -1 to disable).");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -75,7 +87,7 @@
             getErrPrintWriter().println("Error: no brightness specified");
             return 1;
         }
-        float brightness = -1;
+        float brightness = -1.0f;
         try {
             brightness = Float.parseFloat(brightnessText);
         } catch (NumberFormatException e) {
@@ -84,7 +96,7 @@
             getErrPrintWriter().println("Error: brightness should be a number between 0 and 1");
             return 1;
         }
-        mService.setBrightness((int) brightness * 255);
+        mService.setBrightness((int) (brightness * 255));
         return 0;
     }
 
@@ -97,4 +109,26 @@
         mService.setAutoBrightnessLoggingEnabled(enabled);
         return 0;
     }
+
+    private int setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+        mService.setDisplayWhiteBalanceLoggingEnabled(enabled);
+        return 0;
+    }
+
+    private int setAmbientColorTemperatureOverride() {
+        String cctText = getNextArg();
+        if (cctText == null) {
+            getErrPrintWriter().println("Error: no cct specified");
+            return 1;
+        }
+        float cct;
+        try {
+            cct = Float.parseFloat(cctText);
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println("Error: cct should be a number");
+            return 1;
+        }
+        mService.setAmbientColorTemperatureOverride(cct);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index db3928e..dc5be6a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -52,6 +52,9 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
@@ -78,7 +81,8 @@
  * For debugging, you can make the color fade and brightness animations run
  * slower by changing the "animator duration scale" option in Development Settings.
  */
-final class DisplayPowerController implements AutomaticBrightnessController.Callbacks {
+final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
+        DisplayWhiteBalanceController.Callbacks {
     private static final String TAG = "DisplayPowerController";
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
@@ -307,6 +311,12 @@
     // Whether or not to skip the initial brightness ramps into STATE_ON.
     private final boolean mSkipScreenOnBrightnessRamp;
 
+    // Display white balance components.
+    @Nullable
+    private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
+    @Nullable
+    private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
@@ -504,6 +514,20 @@
         mPendingScreenBrightnessSetting = -1;
         mTemporaryAutoBrightnessAdjustment = Float.NaN;
         mPendingAutoBrightnessAdjustment = Float.NaN;
+
+        DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
+        DisplayWhiteBalanceController displayWhiteBalanceController = null;
+        try {
+            displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
+            displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
+                    mSensorManager, resources);
+            displayWhiteBalanceSettings.setCallbacks(this);
+            displayWhiteBalanceController.setCallbacks(this);
+        } catch (Exception e) {
+            Slog.e(TAG, "failed to set up display white-balance: " + e);
+        }
+        mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
+        mDisplayWhiteBalanceController = displayWhiteBalanceController;
     }
 
     /**
@@ -526,6 +550,9 @@
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
+        if (mDisplayWhiteBalanceSettings != null) {
+            mDisplayWhiteBalanceSettings.onSwitchUser();
+        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -592,6 +619,9 @@
     }
 
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        if (mAutomaticBrightnessController == null) {
+            return null;
+        }
         return mAutomaticBrightnessController.getDefaultConfig();
     }
 
@@ -985,6 +1015,16 @@
 
         }
 
+        // Update display white-balance.
+        if (mDisplayWhiteBalanceController != null) {
+            if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
+                mDisplayWhiteBalanceController.setEnabled(true);
+                mDisplayWhiteBalanceController.updateScreenColorTemperature();
+            } else {
+                mDisplayWhiteBalanceController.setEnabled(false);
+            }
+        }
+
         // Determine whether the display is ready for use in the newly requested state.
         // Note that we do not wait for the brightness ramp animation to complete before
         // reporting the display is ready because we only need to ensure the screen is in the
@@ -1699,6 +1739,12 @@
             pw.println();
             mBrightnessTracker.dump(pw);
         }
+
+        pw.println();
+        if (mDisplayWhiteBalanceController != null) {
+            mDisplayWhiteBalanceController.dump(pw);
+            mDisplayWhiteBalanceSettings.dump(pw);
+        }
     }
 
     private static String proximityToString(int state) {
@@ -1845,4 +1891,26 @@
             mAutomaticBrightnessController.setLoggingEnabled(enabled);
         }
     }
+
+    @Override // DisplayWhiteBalanceController.Callbacks
+    public void updateWhiteBalance() {
+        sendUpdatePowerState();
+    }
+
+    void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+        if (mDisplayWhiteBalanceController != null) {
+            mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
+            mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
+        }
+    }
+
+    void setAmbientColorTemperatureOverride(float cct) {
+        if (mDisplayWhiteBalanceController != null) {
+            mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
+            // The ambient color temperature override is only applied when the ambient color
+            // temperature changes or is updated, so it doesn't necessarily change the screen color
+            // temperature immediately. So, let's make it!
+            sendUpdatePowerState();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/utils/History.java b/services/core/java/com/android/server/display/utils/History.java
new file mode 100644
index 0000000..ed171b8
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/History.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.utils;
+
+import java.time.Clock;
+
+/**
+ * A fixed-size buffer that keeps the most recent values and their times.
+ *
+ * This class is used for logging and debugging purposes only, so there's no way to retrieve the
+ * history other than toString(), and a non-monotonic clock is good enough.
+ */
+public class History {
+
+    private int mSize;
+    private int mCount;
+    private int mStart;
+    private int mEnd;
+
+    private long[] mTimes;
+    private float[] mValues;
+
+    private Clock mClock;
+
+    /**
+     * @param size
+     *      The maximum number of values kept.
+     */
+    public History(int size) {
+        this(size, Clock.systemUTC());
+    }
+
+    /**
+     * @param size
+     *    The maximum number of values kept.
+     * @param clock
+     *    The clock used.
+     */
+    public History(int size, Clock clock) {
+        mSize = size;
+        mCount = 0;
+        mStart = 0;
+        mEnd = 0;
+        mTimes = new long[size];
+        mValues = new float[size];
+        mClock = clock;
+    }
+
+    /**
+     * Add a value.
+     *
+     * @param value
+     *      The value.
+     */
+    public void add(float value) {
+        mTimes[mEnd] = mClock.millis();
+        mValues[mEnd] = value;
+        if (mCount < mSize) {
+            mCount++;
+        } else {
+            mStart = (mStart + 1) % mSize;
+        }
+        mEnd = (mEnd + 1) % mSize;
+    }
+
+    /**
+     * Convert the buffer to string.
+     *
+     * @return The buffer as string.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (int i = 0; i < mCount; i++) {
+            final int index = (mStart + i) % mSize;
+            final long time = mTimes[index];
+            final float value = mValues[index];
+            sb.append(value + " @ " + time);
+            if (i + 1 != mCount) {
+                sb.append(", ");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/utils/RollingBuffer.java b/services/core/java/com/android/server/display/utils/RollingBuffer.java
new file mode 100644
index 0000000..dd5b7ab
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/RollingBuffer.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.utils;
+
+/**
+ * A buffer that supports adding new values and truncating old ones.
+ */
+public class RollingBuffer {
+
+    private static final int INITIAL_SIZE = 50;
+
+    private int mSize;
+    private int mCount;
+    private int mStart;
+    private int mEnd;
+
+    private long[] mTimes; // Milliseconds
+    private float[] mValues;
+
+    public RollingBuffer() {
+        mSize = INITIAL_SIZE;
+        mTimes = new long[INITIAL_SIZE];
+        mValues = new float[INITIAL_SIZE];
+        clear();
+    }
+
+    /**
+     * Add a value at a given time.
+     *
+     * @param time
+     *      The time (in milliseconds).
+     * @param value
+     *      The value.
+     */
+    public void add(long time, float value) {
+        if (mCount >= mSize) {
+            expandBuffer();
+        }
+        mTimes[mEnd] = time;
+        mValues[mEnd] = value;
+        mEnd = (mEnd + 1) % mSize;
+        mCount++;
+    }
+
+    /**
+     * Get the size of the buffer.
+     *
+     * @return The size of the buffer.
+     */
+    public int size() {
+        return mCount;
+    }
+
+    /**
+     * Return whether the buffer is empty or not.
+     *
+     * @return Whether the buffer is empty or not.
+     */
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
+     * Get a time.
+     *
+     * @param index
+     *      The index of the time.
+     *
+     * @return The time.
+     */
+    public long getTime(int index) {
+        return mTimes[offsetOf(index)];
+    }
+
+    /**
+     * Get a value.
+     *
+     * @param index
+     *      The index of the value.
+     *
+     * @return The value.
+     */
+    public float getValue(int index) {
+        return mValues[offsetOf(index)];
+    }
+
+    /**
+     * Truncate old values.
+     *
+     * @param minTime
+     *      The minimum time (all values older than this time are truncated).
+     */
+    public void truncate(long minTime) {
+        if (isEmpty() || getTime(0) >= minTime) {
+            return;
+        }
+        final int index = getLatestIndexBefore(minTime);
+        mStart = offsetOf(index);
+        mCount -= index;
+        // Remove everything that happened before mTimes[index], but set the index-th value time to
+        // minTime rather than dropping it, as that would've been the value between the minTime and
+        // mTimes[index+1].
+        //
+        // -*---*---|---*---*- => xxxxxxxxx|*--*---*- rather than xxxxxxxxx|???*---*-
+        //      ^       ^                   ^  ^                               ^
+        //      i      i+1                  i i+1                             i+1
+        mTimes[mStart] = minTime;
+    }
+
+    /**
+     * Clears the buffer.
+     */
+    public void clear() {
+        mCount = 0;
+        mStart = 0;
+        mEnd = 0;
+    }
+
+    /**
+     * Convert the buffer to string.
+     *
+     * @return The buffer as string.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (int i = 0; i < mCount; i++) {
+            final int index = offsetOf(i);
+            sb.append(mValues[index] + " @ " + mTimes[index]);
+            if (i + 1 != mCount) {
+                sb.append(", ");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    private int offsetOf(int index) {
+        if (index < 0 || index >= mCount) {
+            throw new ArrayIndexOutOfBoundsException("invalid index: " + index + ", mCount= "
+                    + mCount);
+        }
+        return (mStart + index) % mSize;
+    }
+
+    private void expandBuffer() {
+        final int size = mSize * 2;
+        long[] times = new long[size];
+        float[] values = new float[size];
+        System.arraycopy(mTimes, mStart, times, 0, mCount - mStart);
+        System.arraycopy(mTimes, 0, times, mCount - mStart, mStart);
+        System.arraycopy(mValues, mStart, values, 0, mCount - mStart);
+        System.arraycopy(mValues, 0, values, mCount - mStart, mStart);
+        mSize = size;
+        mStart = 0;
+        mEnd = mCount;
+        mTimes = times;
+        mValues = values;
+    }
+
+    private int getLatestIndexBefore(long time) {
+        for (int i = 1; i < mCount; i++) {
+            if (mTimes[offsetOf(i)] > time) {
+                return i - 1;
+            }
+        }
+        return mCount - 1;
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
new file mode 100644
index 0000000..532bbed
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.util.Slog;
+
+import com.android.server.display.utils.RollingBuffer;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * The DisplayWhiteBalanceController uses the AmbientFilter to average ambient changes over time,
+ * filter out the noise, and arrive at an estimate of the actual value.
+ *
+ * When the DisplayWhiteBalanceController detects a change in ambient brightness or color
+ * temperature, it passes it to the AmbientFilter, and when it needs the actual ambient value, it
+ * asks it for an estimate.
+ *
+ * Implementations:
+ * - {@link WeightedMovingAverageAmbientFilter}
+ *   A weighted average prioritising recent changes.
+ */
+abstract class AmbientFilter {
+
+    protected static final boolean DEBUG = false; // Enable for verbose logs.
+
+    protected final String mTag;
+    protected boolean mLoggingEnabled;
+
+    // How long ambient value changes are kept and taken into consideration.
+    private final int mHorizon; // Milliseconds
+
+    private final RollingBuffer mBuffer;
+
+    /**
+     * @param tag
+     *      The tag used for dumping and logging.
+     * @param horizon
+     *      How long ambient value changes are kept and taken into consideration.
+     *
+     * @throws IllegalArgumentException
+     *      - horizon is not positive.
+     */
+    AmbientFilter(String tag, int horizon) {
+        validateArguments(horizon);
+        mTag = tag;
+        mLoggingEnabled = false;
+        mHorizon = horizon;
+        mBuffer = new RollingBuffer();
+    }
+
+    /**
+     * Add an ambient value change.
+     *
+     * @param time
+     *      The time.
+     * @param value
+     *      The ambient value.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean addValue(long time, float value) {
+        if (value < 0.0f) {
+            return false;
+        }
+        truncateOldValues(time);
+        if (mLoggingEnabled) {
+            Slog.d(mTag, "add value: " + value + " @ " + time);
+        }
+        mBuffer.add(time, value);
+        return true;
+    }
+
+    /**
+     * Get an estimate of the actual ambient color temperature.
+     *
+     * @param time
+     *      The time.
+     *
+     * @return An estimate of the actual ambient color temperature.
+     */
+    public float getEstimate(long time) {
+        truncateOldValues(time);
+        final float value = filter(time, mBuffer);
+        if (mLoggingEnabled) {
+            Slog.d(mTag, "get estimate: " + value + " @ " + time);
+        }
+        return value;
+    }
+
+    /**
+     * Clears the filter state.
+     */
+    public void clear() {
+        mBuffer.clear();
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging is on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Dump the state.
+     *
+     * @param writer
+     *      The PrintWriter used to dump the state.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("  " + mTag);
+        writer.println("    mLoggingEnabled=" + mLoggingEnabled);
+        writer.println("    mHorizon=" + mHorizon);
+        writer.println("    mBuffer=" + mBuffer);
+    }
+
+    private void validateArguments(int horizon) {
+        if (horizon <= 0) {
+            throw new IllegalArgumentException("horizon must be positive");
+        }
+    }
+
+    private void truncateOldValues(long time) {
+        final long minTime = time - mHorizon;
+        mBuffer.truncate(minTime);
+    }
+
+    protected abstract float filter(long time, RollingBuffer buffer);
+
+    /**
+     * A weighted average prioritising recent changes.
+     */
+    static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
+
+        // How long the latest ambient value change is predicted to last.
+        private static final int PREDICTION_TIME = 100; // Milliseconds
+
+        // Recent changes are prioritised by integrating their duration over y = x + mIntercept
+        // (the higher it is, the less prioritised recent changes are).
+        private final float mIntercept;
+
+        /**
+         * @param tag
+         *      The tag used for dumping and logging.
+         * @param horizon
+         *      How long ambient value changes are kept and taken into consideration.
+         * @param intercept
+         *      Recent changes are prioritised by integrating their duration over y = x + intercept
+         *      (the higher it is, the less prioritised recent changes are).
+         *
+         * @throws IllegalArgumentException
+         *      - horizon is not positive.
+         *      - intercept is NaN or negative.
+         */
+        WeightedMovingAverageAmbientFilter(String tag, int horizon, float intercept) {
+            super(tag, horizon);
+            validateArguments(intercept);
+            mIntercept = intercept;
+        }
+
+        /**
+         * See {@link AmbientFilter#dump base class}.
+         */
+        @Override
+        public void dump(PrintWriter writer) {
+            super.dump(writer);
+            writer.println("    mIntercept=" + mIntercept);
+        }
+
+        // Normalise the times to [t1=0, t2, ..., tN, now + PREDICTION_TIME], so the first change
+        // starts at 0 and the last change is predicted to last a bit, and divide them by 1000 as
+        // milliseconds are high enough to overflow.
+        // The weight of the value from t[i] to t[i+1] is the area under (A.K.A. the integral of)
+        // y = x + mIntercept from t[i] to t[i+1].
+        @Override
+        protected float filter(long time, RollingBuffer buffer) {
+            if (buffer.isEmpty()) {
+                return -1.0f;
+            }
+            float total = 0.0f;
+            float totalWeight = 0.0f;
+            final float[] weights = getWeights(time, buffer);
+            if (DEBUG && mLoggingEnabled) {
+                Slog.v(mTag, "filter: " + buffer + " => " + Arrays.toString(weights));
+            }
+            for (int i = 0; i < weights.length; i++) {
+                final float value = buffer.getValue(i);
+                final float weight = weights[i];
+                total += weight * value;
+                totalWeight += weight;
+            }
+            if (totalWeight == 0.0f) {
+                return buffer.getValue(buffer.size() - 1);
+            }
+            return total / totalWeight;
+        }
+
+        private void validateArguments(float intercept) {
+            if (Float.isNaN(intercept) || intercept < 0.0f) {
+                throw new IllegalArgumentException("intercept must be a non-negative number");
+            }
+        }
+
+        private float[] getWeights(long time, RollingBuffer buffer) {
+            float[] weights = new float[buffer.size()];
+            final long startTime = buffer.getTime(0);
+            float previousTime = 0.0f;
+            for (int i = 1; i < weights.length; i++) {
+                final float currentTime = (buffer.getTime(i) - startTime) / 1000.0f;
+                final float weight = calculateIntegral(previousTime, currentTime);
+                weights[i - 1] = weight;
+                previousTime = currentTime;
+            }
+            final float lastTime = (time + PREDICTION_TIME - startTime) / 1000.0f;
+            final float lastWeight = calculateIntegral(previousTime, lastTime);
+            weights[weights.length - 1] = lastWeight;
+            return weights;
+        }
+
+        private float calculateIntegral(float from, float to) {
+            return antiderivative(to) - antiderivative(from);
+        }
+
+        private float antiderivative(float x) {
+            // f(x) = x + c => F(x) = 1/2 * x^2 + c * x
+            return 0.5f * x * x + mIntercept * x;
+        }
+
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
new file mode 100644
index 0000000..1707a62
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.display.utils.History;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceController uses the AmbientSensor to detect changes in the ambient
+ * brightness and color temperature.
+ *
+ * The AmbientSensor listens on an actual sensor, derives the ambient brightness or color
+ * temperature from its events, and calls back into the DisplayWhiteBalanceController to report it.
+ */
+abstract class AmbientSensor {
+
+    protected String mTag;
+    protected boolean mLoggingEnabled;
+
+    private final Handler mHandler;
+
+    protected final SensorManager mSensorManager;
+    protected Sensor mSensor;
+
+    private boolean mEnabled;
+
+    private int mRate; // Milliseconds
+
+    // The total events count and the most recent events are kept for debugging purposes.
+    private int mEventsCount;
+    private static final int HISTORY_SIZE = 50;
+    private History mEventsHistory;
+
+    /**
+     * @param tag
+     *      The tag used for dumping and logging.
+     * @param handler
+     *      The handler used to determine which thread to run on.
+     * @param sensorManager
+     *      The sensor manager used to acquire necessary sensors.
+     * @param rate
+     *      The sensor rate.
+     *
+     * @throws IllegalArgumentException
+     *      - rate is not positive.
+     * @throws NullPointerException
+     *      - handler is null;
+     *      - sensorManager is null.
+     * @throws IllegalStateException
+     *      - Cannot find the necessary sensor.
+     */
+    AmbientSensor(String tag, @NonNull Handler handler, @NonNull SensorManager sensorManager,
+            int rate) {
+        validateArguments(handler, sensorManager, rate);
+        mTag = tag;
+        mLoggingEnabled = false;
+        mHandler = handler;
+        mSensorManager = sensorManager;
+        mEnabled = false;
+        mRate = rate;
+        mEventsCount = 0;
+        mEventsHistory = new History(HISTORY_SIZE);
+    }
+
+    /**
+     * Enable/disable the sensor.
+     *
+     * @param enabled
+     *      Whether the sensor should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setEnabled(boolean enabled) {
+        if (enabled) {
+            return enable();
+        } else {
+            return disable();
+        }
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Dump the state.
+     *
+     * @param writer
+     *      The PrintWriter used to dump the state.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("  " + mTag);
+        writer.println("    mLoggingEnabled=" + mLoggingEnabled);
+        writer.println("    mHandler=" + mHandler);
+        writer.println("    mSensorManager=" + mSensorManager);
+        writer.println("    mSensor=" + mSensor);
+        writer.println("    mEnabled=" + mEnabled);
+        writer.println("    mRate=" + mRate);
+        writer.println("    mEventsCount=" + mEventsCount);
+        writer.println("    mEventsHistory=" + mEventsHistory);
+    }
+
+
+    private static void validateArguments(Handler handler, SensorManager sensorManager, int rate) {
+        Preconditions.checkNotNull(handler, "handler cannot be null");
+        Preconditions.checkNotNull(sensorManager, "sensorManager cannot be null");
+        if (rate <= 0) {
+            throw new IllegalArgumentException("rate must be positive");
+        }
+    }
+
+    protected abstract void update(float value);
+
+    private boolean enable() {
+        if (mEnabled) {
+            return false;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(mTag, "enabling");
+        }
+        mEnabled = true;
+        startListening();
+        return true;
+    }
+
+    private boolean disable() {
+        if (!mEnabled) {
+            return false;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(mTag, "disabling");
+        }
+        mEnabled = false;
+        mEventsCount = 0;
+        stopListening();
+        return true;
+    }
+
+    private void startListening() {
+        if (mSensorManager == null) {
+            return;
+        }
+        mSensorManager.registerListener(mListener, mSensor, mRate * 1000, mHandler);
+    }
+
+    private void stopListening() {
+        if (mSensorManager == null) {
+            return;
+        }
+        mSensorManager.unregisterListener(mListener);
+    }
+
+    private void handleNewEvent(float value) {
+        // This shouldn't really happen, except for the race condition where the sensor is disabled
+        // with an event already in the handler queue, in which case we discard that event.
+        if (!mEnabled) {
+            return;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(mTag, "handle new event: " + value);
+        }
+        mEventsCount++;
+        mEventsHistory.add(value);
+        update(value);
+    }
+
+    private SensorEventListener mListener = new SensorEventListener() {
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            final float value = event.values[0];
+            handleNewEvent(value);
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Not used.
+        }
+
+    };
+
+    /**
+     * A sensor that reports the ambient brightness.
+     */
+    static class AmbientBrightnessSensor extends AmbientSensor {
+
+        private static final String TAG = "AmbientBrightnessSensor";
+
+        // To decouple the DisplayWhiteBalanceController from the AmbientBrightnessSensor, the
+        // DWBC implements Callbacks and passes itself to the ABS so it can call back into it
+        // without knowing about it.
+        @Nullable
+        private Callbacks mCallbacks;
+
+        /**
+         * @param handler
+         *      The handler used to determine which thread to run on.
+         * @param sensorManager
+         *      The sensor manager used to acquire necessary sensors.
+         * @param rate
+         *      The sensor rate.
+         *
+         * @throws IllegalArgumentException
+         *      - rate is not positive.
+         * @throws NullPointerException
+         *      - handler is null;
+         *      - sensorManager is null.
+         * @throws IllegalStateException
+         *      - Cannot find the light sensor.
+         */
+        AmbientBrightnessSensor(@NonNull Handler handler, @NonNull SensorManager sensorManager,
+                int rate) {
+            super(TAG, handler, sensorManager, rate);
+            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+            if (mSensor == null) {
+                throw new IllegalStateException("cannot find light sensor");
+            }
+            mCallbacks = null;
+        }
+
+        /**
+         * Set an object to call back to when the ambient brightness changes.
+         *
+         * @param callbacks
+         *      The object to call back to.
+         *
+         * @return Whether the method succeeded or not.
+         */
+        public boolean setCallbacks(Callbacks callbacks) {
+            if (mCallbacks == callbacks) {
+                return false;
+            }
+            mCallbacks = callbacks;
+            return true;
+        }
+
+        /**
+         * See {@link AmbientSensor#dump base class}.
+         */
+        @Override
+        public void dump(PrintWriter writer) {
+            super.dump(writer);
+            writer.println("    mCallbacks=" + mCallbacks);
+        }
+
+        interface Callbacks {
+            void onAmbientBrightnessChanged(float value);
+        }
+
+        @Override
+        protected void update(float value) {
+            if (mCallbacks != null) {
+                mCallbacks.onAmbientBrightnessChanged(value);
+            }
+        }
+
+    }
+
+    /**
+     * A sensor that reports the ambient color temperature.
+     */
+    static class AmbientColorTemperatureSensor extends AmbientSensor {
+
+        private static final String TAG = "AmbientColorTemperatureSensor";
+
+        // To decouple the DisplayWhiteBalanceController from the
+        // AmbientColorTemperatureSensor, the DWBC implements Callbacks and passes itself to the
+        // ACTS so it can call back into it without knowing about it.
+        @Nullable
+        private Callbacks mCallbacks;
+
+        /**
+         * @param handler
+         *      The handler used to determine which thread to run on.
+         * @param sensorManager
+         *      The sensor manager used to acquire necessary sensors.
+         * @param name
+         *      The color sensor name.
+         * @param rate
+         *      The sensor rate.
+         *
+         * @throws IllegalArgumentException
+         *      - rate is not positive.
+         * @throws NullPointerException
+         *      - handler is null;
+         *      - sensorManager is null.
+         * @throws IllegalStateException
+         *      - Cannot find the color sensor.
+         */
+        AmbientColorTemperatureSensor(@NonNull Handler handler,
+                @NonNull SensorManager sensorManager, String name, int rate) {
+            super(TAG, handler, sensorManager, rate);
+            mSensor = null;
+            for (Sensor sensor : mSensorManager.getSensorList(Sensor.TYPE_ALL)) {
+                if (sensor.getStringType().equals(name)) {
+                    mSensor = sensor;
+                    break;
+                }
+            }
+            if (mSensor == null) {
+                throw new IllegalStateException("cannot find sensor " + name);
+            }
+            mCallbacks = null;
+        }
+
+        /**
+         * Set an object to call back to when the ambient color temperature changes.
+         *
+         * @param callbacks
+         *      The object to call back to.
+         *
+         * @return Whether the method succeeded or not.
+         */
+        public boolean setCallbacks(Callbacks callbacks) {
+            if (mCallbacks == callbacks) {
+                return false;
+            }
+            mCallbacks = callbacks;
+            return true;
+        }
+
+        /**
+         * See {@link AmbientSensor#dump base class}.
+         */
+        @Override
+        public void dump(PrintWriter writer) {
+            super.dump(writer);
+            writer.println("    mCallbacks=" + mCallbacks);
+        }
+
+        interface Callbacks {
+            void onAmbientColorTemperatureChanged(float value);
+        }
+
+        @Override
+        protected void update(float value) {
+            if (mCallbacks != null) {
+                mCallbacks.onAmbientColorTemperatureChanged(value);
+            }
+        }
+
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
new file mode 100644
index 0000000..7ae00af
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.utils.History;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
+ * screen color temperature depending on the ambient color temperature).
+ *
+ * The DisplayWhiteBalanceController:
+ * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
+ * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
+ *   noise, and arrive at an estimate of the actual ambient color temperature;
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
+ *   updated, suppressing changes that are too frequent or too minor.
+ */
+public class DisplayWhiteBalanceController implements
+        AmbientSensor.AmbientBrightnessSensor.Callbacks,
+        AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
+
+    protected static final String TAG = "DisplayWhiteBalanceController";
+    protected boolean mLoggingEnabled;
+
+    private boolean mEnabled;
+
+    // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
+    // implements Callbacks and passes itself to the DWBC so it can call back into it without
+    // knowing about it.
+    private Callbacks mCallbacks;
+
+    private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
+    private AmbientFilter mBrightnessFilter;
+    private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
+    private AmbientFilter mColorTemperatureFilter;
+    private DisplayWhiteBalanceThrottler mThrottler;
+
+    // When the brightness drops below a certain threshold, it affects the color temperature
+    // accuracy, so we fall back to a fixed ambient color temperature.
+    private final float mLowLightAmbientBrightnessThreshold;
+    private final float mLowLightAmbientColorTemperature;
+
+    private float mAmbientColorTemperature;
+    private float mPendingAmbientColorTemperature;
+    private float mLastAmbientColorTemperature;
+
+    private ColorDisplayServiceInternal mColorDisplayServiceInternal;
+
+    // The most recent ambient color temperature values are kept for debugging purposes.
+    private static final int HISTORY_SIZE = 50;
+    private History mAmbientColorTemperatureHistory;
+
+    // Override the ambient color temperature for debugging purposes.
+    private float mAmbientColorTemperatureOverride;
+
+    /**
+     * @param brightnessSensor
+     *      The sensor used to detect changes in the ambient brightness.
+     * @param brightnessFilter
+     *      The filter used to avergae ambient brightness changes over time, filter out the noise
+     *      and arrive at an estimate of the actual ambient brightness.
+     * @param colorTemperatureSensor
+     *      The sensor used to detect changes in the ambient color temperature.
+     * @param colorTemperatureFilter
+     *      The filter used to average ambient color temperature changes over time, filter out the
+     *      noise and arrive at an estimate of the actual ambient color temperature.
+     * @param throttler
+     *      The throttler used to determine whether the new screen color temperature should be
+     *      updated or not.
+     * @param lowLightAmbientBrightnessThreshold
+     *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
+     *      temperature.
+     * @param lowLightAmbientColorTemperature
+     *      The ambient color temperature to which we fall back when the ambient brightness drops
+     *      beneath a certain threshold.
+     *
+     * @throws NullPointerException
+     *      - brightnessSensor is null;
+     *      - brightnessFilter is null;
+     *      - colorTemperatureSensor is null;
+     *      - colorTemperatureFilter is null;
+     *      - throttler is null.
+     */
+    public DisplayWhiteBalanceController(
+            @NonNull AmbientSensor.AmbientBrightnessSensor brightnessSensor,
+            @NonNull AmbientFilter brightnessFilter,
+            @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
+            @NonNull AmbientFilter colorTemperatureFilter,
+            @NonNull DisplayWhiteBalanceThrottler throttler,
+            float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature) {
+        validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
+                colorTemperatureFilter, throttler);
+        mLoggingEnabled = false;
+        mEnabled = false;
+        mCallbacks = null;
+        mBrightnessSensor = brightnessSensor;
+        mBrightnessFilter = brightnessFilter;
+        mColorTemperatureSensor = colorTemperatureSensor;
+        mColorTemperatureFilter = colorTemperatureFilter;
+        mThrottler = throttler;
+        mLowLightAmbientBrightnessThreshold = lowLightAmbientBrightnessThreshold;
+        mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+        mAmbientColorTemperature = -1.0f;
+        mPendingAmbientColorTemperature = -1.0f;
+        mLastAmbientColorTemperature = -1.0f;
+        mAmbientColorTemperatureHistory = new History(HISTORY_SIZE);
+        mAmbientColorTemperatureOverride = -1.0f;
+        mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
+    }
+
+    /**
+     * Enable/disable the controller.
+     *
+     * @param enabled
+     *      Whether the controller should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setEnabled(boolean enabled) {
+        if (enabled) {
+            return enable();
+        } else {
+            return disable();
+        }
+    }
+
+    /**
+     * Set an object to call back to when the screen color temperature should be updated.
+     *
+     * @param callbacks
+     *      The object to call back to.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setCallbacks(Callbacks callbacks) {
+        if (mCallbacks == callbacks) {
+            return false;
+        }
+        mCallbacks = callbacks;
+        return true;
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        mBrightnessSensor.setLoggingEnabled(loggingEnabled);
+        mBrightnessFilter.setLoggingEnabled(loggingEnabled);
+        mColorTemperatureSensor.setLoggingEnabled(loggingEnabled);
+        mColorTemperatureFilter.setLoggingEnabled(loggingEnabled);
+        mThrottler.setLoggingEnabled(loggingEnabled);
+        return true;
+    }
+
+    /**
+     * Set the ambient color temperature override.
+     *
+     * This is only applied when the ambient color temperature changes or is updated (in which case
+     * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
+     * change the screen color temperature immediately.
+     *
+     * @param ambientColorTemperatureOverride
+     *      The ambient color temperature override.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride) {
+        if (mAmbientColorTemperatureOverride == ambientColorTemperatureOverride) {
+            return false;
+        }
+        mAmbientColorTemperatureOverride = ambientColorTemperatureOverride;
+        return true;
+    }
+
+    /**
+     * Dump the state.
+     *
+     * @param writer
+     *      The writer used to dump the state.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("DisplayWhiteBalanceController");
+        writer.println("  mLoggingEnabled=" + mLoggingEnabled);
+        writer.println("  mEnabled=" + mEnabled);
+        writer.println("  mCallbacks=" + mCallbacks);
+        mBrightnessSensor.dump(writer);
+        mBrightnessFilter.dump(writer);
+        mColorTemperatureSensor.dump(writer);
+        mColorTemperatureFilter.dump(writer);
+        mThrottler.dump(writer);
+        writer.println("  mLowLightAmbientBrightnessThreshold="
+                + mLowLightAmbientBrightnessThreshold);
+        writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+        writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
+        writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
+        writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
+        writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
+        writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
+    }
+
+    @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
+    public void onAmbientBrightnessChanged(float value) {
+        final long time = System.currentTimeMillis();
+        mBrightnessFilter.addValue(time, value);
+        updateAmbientColorTemperature();
+    }
+
+    @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
+    public void onAmbientColorTemperatureChanged(float value) {
+        final long time = System.currentTimeMillis();
+        mColorTemperatureFilter.addValue(time, value);
+        updateAmbientColorTemperature();
+    }
+
+    /**
+     * Updates the ambient color temperature.
+     */
+    public void updateAmbientColorTemperature() {
+        final long time = System.currentTimeMillis();
+        float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
+
+        final float ambientBrightness = mBrightnessFilter.getEstimate(time);
+        if (ambientBrightness < mLowLightAmbientBrightnessThreshold) {
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "low light ambient brightness: " + ambientBrightness + " < "
+                        + mLowLightAmbientBrightnessThreshold
+                        + ", falling back to fixed ambient color temperature: "
+                        + ambientColorTemperature + " => " + mLowLightAmbientColorTemperature);
+            }
+            ambientColorTemperature = mLowLightAmbientColorTemperature;
+        }
+
+        if (mAmbientColorTemperatureOverride != -1.0f) {
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
+                        + " => " + mAmbientColorTemperatureOverride);
+            }
+            ambientColorTemperature = mAmbientColorTemperatureOverride;
+        }
+
+        // When the screen color temperature needs to be updated, we call DisplayPowerController to
+        // call our updateColorTemperature. The reason we don't call it directly is that we want
+        // all changes to the system to happen in a predictable order in DPC's main loop
+        // (updatePowerState).
+        if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
+            return;
+        }
+
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
+        }
+        mPendingAmbientColorTemperature = ambientColorTemperature;
+        if (mCallbacks != null) {
+            mCallbacks.updateWhiteBalance();
+        }
+    }
+
+    /**
+     * Updates the screen color temperature.
+     */
+    public void updateScreenColorTemperature() {
+        float ambientColorTemperature = -1.0f;
+
+        // If both the pending and the current ambient color temperatures are -1, it means the DWBC
+        // was just enabled, and we use the last ambient color temperature until new sensor events
+        // give us a better estimate.
+        if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
+            ambientColorTemperature = mLastAmbientColorTemperature;
+        }
+
+        // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
+        // and different than the current one.
+        if (mPendingAmbientColorTemperature != -1.0f
+                && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
+            ambientColorTemperature = mPendingAmbientColorTemperature;
+        }
+
+        if (ambientColorTemperature == -1.0f) {
+            return;
+        }
+
+        mAmbientColorTemperature = ambientColorTemperature;
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
+        }
+        mPendingAmbientColorTemperature = -1.0f;
+        mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
+        mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
+                (int) mAmbientColorTemperature);
+        mLastAmbientColorTemperature = mAmbientColorTemperature;
+    }
+
+    /**
+     * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
+     * by providing this interface to implement (and a method to set its callbacks object), and
+     * calling these methods.
+     */
+    public interface Callbacks {
+
+        /**
+         * Called whenever the display white-balance state has changed.
+         *
+         * Usually, this means the estimated ambient color temperature has changed enough, and the
+         * screen color temperature should be updated; but it is also called by
+         */
+        void updateWhiteBalance();
+    }
+
+    private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
+            AmbientFilter brightnessFilter,
+            AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
+            AmbientFilter colorTemperatureFilter,
+            DisplayWhiteBalanceThrottler throttler) {
+        Preconditions.checkNotNull(brightnessSensor, "brightnessSensor must not be null");
+        Preconditions.checkNotNull(brightnessFilter, "brightnessFilter must not be null");
+        Preconditions.checkNotNull(colorTemperatureSensor,
+                "colorTemperatureSensor must not be null");
+        Preconditions.checkNotNull(colorTemperatureFilter,
+                "colorTemperatureFilter must not be null");
+        Preconditions.checkNotNull(throttler, "throttler cannot be null");
+    }
+
+    private boolean enable() {
+        if (mEnabled) {
+            return false;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "enabling");
+        }
+        mEnabled = true;
+        mBrightnessSensor.setEnabled(true);
+        mColorTemperatureSensor.setEnabled(true);
+        return true;
+    }
+
+    private boolean disable() {
+        if (!mEnabled) {
+            return false;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "disabling");
+        }
+        mEnabled = false;
+        mBrightnessSensor.setEnabled(false);
+        mBrightnessFilter.clear();
+        mColorTemperatureSensor.setEnabled(false);
+        mColorTemperatureFilter.clear();
+        mThrottler.clear();
+        mAmbientColorTemperature = -1.0f;
+        mPendingAmbientColorTemperature = -1.0f;
+        return true;
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
new file mode 100644
index 0000000..fd78ddb
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.TypedValue;
+
+/**
+ * The DisplayWhiteBalanceFactory creates and configures an DisplayWhiteBalanceController.
+ */
+public class DisplayWhiteBalanceFactory {
+
+    private static final String BRIGHTNESS_FILTER_TAG = "AmbientBrightnessFilter";
+    private static final String COLOR_TEMPERATURE_FILTER_TAG = "AmbientColorTemperatureFilter";
+
+    /**
+     * Create and configure an DisplayWhiteBalanceController.
+     *
+     * @param handler
+     *      The handler used to determine which thread to run on.
+     * @param sensorManager
+     *      The sensor manager used to acquire necessary sensors.
+     * @param resources
+     *      The resources used to configure the various components.
+     *
+     * @return An DisplayWhiteBalanceController.
+     *
+     * @throws NullPointerException
+     *      - handler is null;
+     *      - sensorManager is null.
+     * @throws Resources.NotFoundException
+     *      - Configurations are missing.
+     * @throws IllegalArgumentException
+     *      - Configurations are invalid.
+     * @throws IllegalStateException
+     *      - Cannot find the necessary sensors.
+     */
+    public static DisplayWhiteBalanceController create(Handler handler,
+            SensorManager sensorManager, Resources resources) {
+        final AmbientSensor.AmbientBrightnessSensor brightnessSensor =
+                createBrightnessSensor(handler, sensorManager, resources);
+        final AmbientFilter brightnessFilter = createBrightnessFilter(resources);
+        final AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor =
+                createColorTemperatureSensor(handler, sensorManager, resources);
+        final AmbientFilter colorTemperatureFilter = createColorTemperatureFilter(resources);
+        final DisplayWhiteBalanceThrottler throttler = createThrottler(resources);
+        final float lowLightAmbientBrightnessThreshold = getFloat(resources,
+                com.android.internal.R.dimen
+                .config_displayWhiteBalanceLowLightAmbientBrightnessThreshold);
+        final float lowLightAmbientColorTemperature = getFloat(resources,
+                com.android.internal.R.dimen
+                .config_displayWhiteBalanceLowLightAmbientColorTemperature);
+        final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
+                brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
+                throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature);
+        brightnessSensor.setCallbacks(controller);
+        colorTemperatureSensor.setCallbacks(controller);
+        return controller;
+    }
+
+    // Instantiation is disabled.
+    private DisplayWhiteBalanceFactory() { }
+
+    private static AmbientSensor.AmbientBrightnessSensor createBrightnessSensor(Handler handler,
+            SensorManager sensorManager, Resources resources) {
+        final int rate = resources.getInteger(
+                com.android.internal.R.integer.config_displayWhiteBalanceBrightnessSensorRate);
+        return new AmbientSensor.AmbientBrightnessSensor(handler, sensorManager, rate);
+    }
+
+    private static AmbientFilter createBrightnessFilter(Resources resources) {
+        final int horizon = resources.getInteger(
+                com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
+        final float intercept = getFloat(resources,
+                com.android.internal.R.dimen.config_displayWhiteBalanceBrightnessFilterIntercept);
+        if (!Float.isNaN(intercept)) {
+            return new AmbientFilter.WeightedMovingAverageAmbientFilter(
+                    BRIGHTNESS_FILTER_TAG, horizon, intercept);
+        }
+        throw new IllegalArgumentException("missing configurations: "
+                + "expected config_displayWhiteBalanceBrightnessFilterIntercept");
+    }
+
+
+    private static AmbientSensor.AmbientColorTemperatureSensor createColorTemperatureSensor(
+            Handler handler, SensorManager sensorManager, Resources resources) {
+        final String name = resources.getString(
+                com.android.internal.R.string
+                .config_displayWhiteBalanceColorTemperatureSensorName);
+        final int rate = resources.getInteger(
+                com.android.internal.R.integer
+                .config_displayWhiteBalanceColorTemperatureSensorRate);
+        return new AmbientSensor.AmbientColorTemperatureSensor(handler, sensorManager, name, rate);
+    }
+
+    private static AmbientFilter createColorTemperatureFilter(Resources resources) {
+        final int horizon = resources.getInteger(
+                com.android.internal.R.integer
+                .config_displayWhiteBalanceColorTemperatureFilterHorizon);
+        final float intercept = getFloat(resources,
+                com.android.internal.R.dimen
+                .config_displayWhiteBalanceColorTemperatureFilterIntercept);
+        if (!Float.isNaN(intercept)) {
+            return new AmbientFilter.WeightedMovingAverageAmbientFilter(
+                    COLOR_TEMPERATURE_FILTER_TAG, horizon, intercept);
+        }
+        throw new IllegalArgumentException("missing configurations: "
+                + "expected config_displayWhiteBalanceColorTemperatureFilterIntercept");
+    }
+
+    private static DisplayWhiteBalanceThrottler createThrottler(Resources resources) {
+        final int increaseDebounce = resources.getInteger(
+                com.android.internal.R.integer.config_displayWhiteBalanceDecreaseDebounce);
+        final int decreaseDebounce = resources.getInteger(
+                com.android.internal.R.integer.config_displayWhiteBalanceIncreaseDebounce);
+        final float[] baseThresholds = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceBaseThresholds);
+        final float[] increaseThresholds = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceIncreaseThresholds);
+        final float[] decreaseThresholds = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceDecreaseThresholds);
+        return new DisplayWhiteBalanceThrottler(increaseDebounce, decreaseDebounce, baseThresholds,
+                increaseThresholds, decreaseThresholds);
+    }
+
+    private static float getFloat(Resources resources, int id) {
+        TypedValue value = new TypedValue();
+        resources.getValue(id, value, true /* resolveRefs */);
+        if (value.type != TypedValue.TYPE_FLOAT) {
+            return Float.NaN;
+        }
+        return value.getFloat();
+    }
+
+    private static float[] getFloatArray(Resources resources, int id) {
+        TypedArray array = resources.obtainTypedArray(id);
+        try {
+            if (array.length() == 0) {
+                return null;
+            }
+            float[] values = new float[array.length()];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = array.getFloat(i, Float.NaN);
+                if (Float.isNaN(values[i])) {
+                    return null;
+                }
+            }
+            return values;
+        } finally {
+            array.recycle();
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
new file mode 100644
index 0000000..a53e91c
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.display.ColorDisplayService;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController.Callbacks;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceSettings holds the state of all the settings related to
+ * display white-balance, and can be used to decide whether to enable the
+ * DisplayWhiteBalanceController.
+ */
+public class DisplayWhiteBalanceSettings implements
+        ColorDisplayService.DisplayWhiteBalanceListener {
+
+    protected static final String TAG = "DisplayWhiteBalanceSettings";
+    protected boolean mLoggingEnabled;
+
+    private static final String SETTING_URI = Secure.DISPLAY_WHITE_BALANCE_ENABLED;
+    private static final int SETTING_DEFAULT = 0;
+    private static final int SETTING_ENABLED = 1;
+
+    private static final int MSG_SET_ACTIVE = 1;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final SettingsObserver mSettingsObserver;
+
+    // To decouple the DisplayPowerController from the DisplayWhiteBalanceSettings, the DPC
+    // implements Callbacks and passes itself to the DWBS so it can call back into it without
+    // knowing about it.
+    private Callbacks mCallbacks;
+
+    private int mSetting;
+    private boolean mActive;
+
+    /**
+     * @param context
+     *      The context in which display white-balance is used.
+     * @param handler
+     *      The handler used to determine which thread to run on.
+     *
+     * @throws NullPointerException
+     *      - context is null;
+     *      - handler is null.
+     */
+    public DisplayWhiteBalanceSettings(@NonNull Context context, @NonNull Handler handler) {
+        validateArguments(context, handler);
+        mLoggingEnabled = false;
+        mContext = context;
+        mHandler = new DisplayWhiteBalanceSettingsHandler(handler.getLooper());
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mSetting = getSetting();
+        mActive = false;
+        mCallbacks = null;
+
+        mContext.getContentResolver().registerContentObserver(
+                Secure.getUriFor(SETTING_URI), false /* notifyForDescendants */, mSettingsObserver,
+                UserHandle.USER_ALL);
+
+        ColorDisplayServiceInternal cds =
+                LocalServices.getService(ColorDisplayServiceInternal.class);
+        cds.setDisplayWhiteBalanceListener(this);
+    }
+
+    /**
+     * Set an object to call back to when the display white balance state should be updated.
+     *
+     * @param callbacks
+     *      The object to call back to.
+     *
+     * @return Whether the method suceeded or not.
+     */
+    public boolean setCallbacks(Callbacks callbacks) {
+        if (mCallbacks == callbacks) {
+            return false;
+        }
+        mCallbacks = callbacks;
+        return true;
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Returns whether display white-balance is enabled.
+     *
+     * @return Whether display white-balance is enabled.
+     */
+    public boolean isEnabled() {
+        return (mSetting == SETTING_ENABLED) && mActive;
+    }
+
+    /**
+     * Re-evaluate state after switching to a new user.
+     */
+    public void onSwitchUser() {
+        handleSettingChange();
+    }
+
+    /**
+     * Dump the state.
+     *
+     * @param writer
+     *      The writer used to dump the state.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("DisplayWhiteBalanceSettings");
+        writer.println("  mLoggingEnabled=" + mLoggingEnabled);
+        writer.println("  mContext=" + mContext);
+        writer.println("  mHandler=" + mHandler);
+        writer.println("  mSettingsObserver=" + mSettingsObserver);
+        writer.println("  mSetting=" + mSetting);
+        writer.println("  mActive=" + mActive);
+        writer.println("  mCallbacks=" + mCallbacks);
+    }
+
+    @Override
+    public void onDisplayWhiteBalanceStatusChanged(boolean active) {
+        Message msg = mHandler.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0);
+        msg.sendToTarget();
+    }
+
+    private void validateArguments(Context context, Handler handler) {
+        Preconditions.checkNotNull(context, "context must not be null");
+        Preconditions.checkNotNull(handler, "handler must not be null");
+    }
+
+    private int getSetting() {
+        return Secure.getIntForUser(mContext.getContentResolver(), SETTING_URI, SETTING_DEFAULT,
+                UserHandle.USER_CURRENT);
+    }
+
+    private void handleSettingChange() {
+        final int setting = getSetting();
+        if (mSetting == setting) {
+            return;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "Setting: " + setting);
+        }
+        mSetting = setting;
+        if (mCallbacks != null) {
+            mCallbacks.updateWhiteBalance();
+        }
+    }
+
+    private void setActive(boolean active) {
+        if (mActive == active) {
+            return;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(TAG, "Active: " + active);
+        }
+        mActive = active;
+        if (mCallbacks != null) {
+            mCallbacks.updateWhiteBalance();
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            handleSettingChange();
+        }
+    }
+
+    private final class DisplayWhiteBalanceSettingsHandler extends Handler {
+        DisplayWhiteBalanceSettingsHandler(Looper looper) {
+            super(looper, null, true /* async */);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_ACTIVE:
+                    setActive(msg.arg1 != 0);
+                    break;
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
new file mode 100644
index 0000000..c1f0e98
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.display.whitebalance;
+
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
+ * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * minor.
+ */
+class DisplayWhiteBalanceThrottler {
+
+    protected static final String TAG = "DisplayWhiteBalanceThrottler";
+    protected boolean mLoggingEnabled;
+
+    private int mIncreaseDebounce;  // Milliseconds
+    private int mDecreaseDebounce;  // Milliseconds
+    private long mLastTime;         // Milliseconds
+
+    private float[] mBaseThresholds;
+    private float[] mIncreaseThresholds;
+    private float[] mDecreaseThresholds;
+    private float mIncreaseThreshold;
+    private float mDecreaseThreshold;
+    private float mLastValue;
+
+    /**
+     * @param increaseDebounce
+     *      The debounce time for increasing (throttled if {@code time < lastTime + debounce}).
+     * @param decreaseDebounce
+     *      The debounce time for decreasing (throttled if {@code time < lastTime + debounce}).
+     * @param baseThresholds
+     *      The ambient color temperature values used to determine the threshold as the
+     *      corresponding value in increaseThresholds/decreaseThresholds.
+     * @param increaseThresholds
+     *      The increase threshold values (throttled if {@code value < value * (1 + threshold)}).
+     * @param decreaseThresholds
+     *      The decrease threshold values (throttled if {@code value > value * (1 - threshold)}).
+     *
+     * @throws IllegalArgumentException
+     *      - increaseDebounce is negative;
+     *      - decreaseDebounce is negative;
+     *      - baseThresholds to increaseThresholds is not a valid mapping*;
+     *      - baseThresholds to decreaseThresholds is not a valid mapping*;
+     *
+     *      (*) The x to y mapping is valid if:
+     *          - x and y are not null;
+     *          - x and y are not empty;
+     *          - x and y contain only non-negative numbers;
+     *          - x is strictly increasing.
+     */
+    DisplayWhiteBalanceThrottler(int increaseDebounce, int decreaseDebounce,
+            float[] baseThresholds, float[] increaseThresholds, float[] decreaseThresholds) {
+        validateArguments(increaseDebounce, decreaseDebounce, baseThresholds, increaseThresholds,
+                decreaseThresholds);
+        mLoggingEnabled = false;
+        mIncreaseDebounce = increaseDebounce;
+        mDecreaseDebounce = decreaseDebounce;
+        mBaseThresholds = baseThresholds;
+        mIncreaseThresholds = increaseThresholds;
+        mDecreaseThresholds = decreaseThresholds;
+        clear();
+    }
+
+    /**
+     * Check whether the ambient color temperature should be throttled.
+     *
+     * @param value
+     *      The ambient color temperature value.
+     *
+     * @return Whether the ambient color temperature should be throttled.
+     */
+    public boolean throttle(float value) {
+        if (mLastTime != -1 && (tooSoon(value) || tooClose(value))) {
+            return true;
+        }
+        computeThresholds(value);
+        mLastTime = System.currentTimeMillis();
+        mLastValue = value;
+        return false;
+    }
+
+    /**
+     * Clears the throttler state.
+     */
+    public void clear() {
+        mLastTime = -1;
+        mIncreaseThreshold = -1.0f;
+        mDecreaseThreshold = -1.0f;
+        mLastValue = -1.0f;
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging is on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Dump the state.
+     *
+     * @param writer
+     *      The PrintWriter used to dump the state.
+     */
+    public void dump(PrintWriter writer) {
+        writer.println("  DisplayWhiteBalanceThrottler");
+        writer.println("    mLoggingEnabled=" + mLoggingEnabled);
+        writer.println("    mIncreaseDebounce=" + mIncreaseDebounce);
+        writer.println("    mDecreaseDebounce=" + mDecreaseDebounce);
+        writer.println("    mLastTime=" + mLastTime);
+        writer.println("    mBaseThresholds=" + Arrays.toString(mBaseThresholds));
+        writer.println("    mIncreaseThresholds=" + Arrays.toString(mIncreaseThresholds));
+        writer.println("    mDecreaseThresholds=" + Arrays.toString(mDecreaseThresholds));
+        writer.println("    mIncreaseThreshold=" + mIncreaseThreshold);
+        writer.println("    mDecreaseThreshold=" + mDecreaseThreshold);
+        writer.println("    mLastValue=" + mLastValue);
+    }
+
+    private void validateArguments(float increaseDebounce, float decreaseDebounce,
+            float[] baseThresholds, float[] increaseThresholds, float[] decreaseThresholds) {
+        if (Float.isNaN(increaseDebounce) || increaseDebounce < 0.0f) {
+            throw new IllegalArgumentException("increaseDebounce must be a non-negative number.");
+        }
+        if (Float.isNaN(decreaseDebounce) || decreaseDebounce < 0.0f) {
+            throw new IllegalArgumentException("decreaseDebounce must be a non-negative number.");
+        }
+        if (!isValidMapping(baseThresholds, increaseThresholds)) {
+            throw new IllegalArgumentException(
+                    "baseThresholds to increaseThresholds is not a valid mapping.");
+        }
+        if (!isValidMapping(baseThresholds, decreaseThresholds)) {
+            throw new IllegalArgumentException(
+                    "baseThresholds to decreaseThresholds is not a valid mapping.");
+        }
+    }
+
+    private static boolean isValidMapping(float[] x, float[] y) {
+        if (x == null || y == null || x.length == 0 || y.length == 0 || x.length != y.length) {
+            return false;
+        }
+        float prevX = -1.0f;
+        for (int i = 0; i < x.length; i++) {
+            if (Float.isNaN(x[i]) || Float.isNaN(y[i]) || x[i] < 0 || prevX >= x[i]) {
+                return false;
+            }
+            prevX = x[i];
+        }
+        return true;
+    }
+
+    private boolean tooSoon(float value) {
+        final long time = System.currentTimeMillis();
+        final long earliestTime;
+        if (value > mLastValue) {
+            earliestTime = mLastTime + mIncreaseDebounce;
+        } else { // value <= mLastValue
+            earliestTime = mLastTime + mDecreaseDebounce;
+        }
+        final boolean tooSoon = time < earliestTime;
+        if (mLoggingEnabled) {
+            Slog.d(TAG, (tooSoon ? "too soon: " : "late enough: ") + time
+                    + (tooSoon ? " < " : " > ") + earliestTime);
+        }
+        return tooSoon;
+    }
+
+    private boolean tooClose(float value) {
+        final float threshold;
+        final boolean tooClose;
+        if (value > mLastValue) {
+            threshold = mIncreaseThreshold;
+            tooClose = value < threshold;
+        } else { // value <= mLastValue
+            threshold = mDecreaseThreshold;
+            tooClose = value > threshold;
+        }
+        if (mLoggingEnabled) {
+            Slog.d(TAG, (tooClose ? "too close: " : "far enough: ") + value
+                    + (value > threshold ? " > " : " < ") + threshold);
+        }
+        return tooClose;
+    }
+
+    private void computeThresholds(float value) {
+        final int index = getHighestIndexBefore(value, mBaseThresholds);
+        mIncreaseThreshold = value * (1.0f + mIncreaseThresholds[index]);
+        mDecreaseThreshold = value * (1.0f - mDecreaseThresholds[index]);
+    }
+
+    private int getHighestIndexBefore(float value, float[] values) {
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] >= value) {
+                return i;
+            }
+        }
+        return values.length - 1;
+    }
+
+}
diff --git a/services/core/java/com/android/server/dreams/OWNERS b/services/core/java/com/android/server/dreams/OWNERS
index 3c9bbf8..426f002 100644
--- a/services/core/java/com/android/server/dreams/OWNERS
+++ b/services/core/java/com/android/server/dreams/OWNERS
@@ -1,3 +1,3 @@
-dsandler@google.com
+dsandler@android.com
 michaelwr@google.com
 roosa@google.com
diff --git a/services/core/java/com/android/server/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java
index 3ebba00..6f2bfc3 100644
--- a/services/core/java/com/android/server/incident/IncidentCompanionService.java
+++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java
@@ -16,38 +16,18 @@
 
 package com.android.server.incident;
 
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.net.Uri;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.IIncidentAuthListener;
 import android.os.IIncidentCompanion;
-import android.os.IncidentManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
 
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
-// TODO: User changes should deny everything that's pending.
-
 /**
  * Helper service for incidentd and dumpstated to provide user feedback
  * and authorization for bug and inicdent reports to be taken.
@@ -55,83 +35,26 @@
 public class IncidentCompanionService extends SystemService {
     static final String TAG = "IncidentCompanionService";
 
-    private final Handler mHandler = new Handler();
-    private final RequestQueue mRequestQueue = new RequestQueue(mHandler);
-    private final PackageManager mPackageManager;
-    private final AppOpsManager mAppOpsManager;
-
-    //
-    // All fields below must be protected by mLock
-    //
-    private final Object mLock = new Object();
-    private final ArrayList<PendingReportRec> mPending = new ArrayList();
-
     /**
-     * The next ID we'll use when we make a PendingReportRec.
+     * Tracker for reports pending approval.
      */
-    private int mNextPendingId = 1;
-
-    /**
-     * One for each authorization that's pending.
-     */
-    private final class PendingReportRec {
-        public int id;
-        public String callingPackage;
-        public int flags;
-        public IIncidentAuthListener listener;
-        public long addedRealtime;
-        public long addedWalltime;
-
-        /**
-         * Construct a PendingReportRec, with an auto-incremented id.
-         */
-        PendingReportRec(String callingPackage, int flags, IIncidentAuthListener listener) {
-            this.id = mNextPendingId++;
-            this.callingPackage = callingPackage;
-            this.flags = flags;
-            this.listener = listener;
-            this.addedRealtime = SystemClock.elapsedRealtime();
-            this.addedWalltime = System.currentTimeMillis();
-        }
-
-        /**
-         * Get the Uri that contains the flattened data.
-         */
-        Uri getUri() {
-            return (new Uri.Builder())
-                    .scheme(IncidentManager.URI_SCHEME)
-                    .authority(IncidentManager.URI_AUTHORITY)
-                    .path(IncidentManager.URI_PATH)
-                    .appendQueryParameter(IncidentManager.URI_PARAM_ID, Integer.toString(id))
-                    .appendQueryParameter(IncidentManager.URI_PARAM_CALLING_PACKAGE, callingPackage)
-                    .appendQueryParameter(IncidentManager.URI_PARAM_FLAGS, Integer.toString(flags))
-                    .appendQueryParameter(IncidentManager.URI_PARAM_TIMESTAMP,
-                            Long.toString(addedWalltime))
-                    .build();
-        }
-    }
+    private PendingReports mPendingReports;
 
     /**
      * Implementation of the IIncidentCompanion binder interface.
      */
     private final class BinderService extends IIncidentCompanion.Stub {
         /**
-         * ONEWAY binder call to initiate authorizing the report.  The actual logic is posted
-         * to mRequestQueue, and may happen later.  The security checks need to happen here.
+         * ONEWAY binder call to initiate authorizing the report.
          */
         @Override
-        public void authorizeReport(int callingUid, final String callingPackage, final int flags,
+        public void authorizeReport(int callingUid, final String callingPackage, int flags,
                 final IIncidentAuthListener listener) {
             enforceRequestAuthorizationPermission();
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                // Starting the system server is complicated, and rather than try to
-                // have a complicated lifecycle that we share with dumpstated and incidentd,
-                // we will accept the request, and then display it whenever it becomes possible to.
-                mRequestQueue.enqueue(listener.asBinder(), true, () -> {
-                    authorizeReportImpl(callingUid, callingPackage, flags, listener);
-                });
+                mPendingReports.authorizeReport(callingUid, callingPackage, flags, listener);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -152,9 +75,7 @@
             // authorize/cancel pairs.
             final long ident = Binder.clearCallingIdentity();
             try {
-                mRequestQueue.enqueue(listener.asBinder(), false, () -> {
-                    cancelReportImpl(listener);
-                });
+                mPendingReports.cancelAuthorization(listener);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -167,19 +88,11 @@
         @Override
         public List<String> getPendingReports() {
             enforceAuthorizePermission();
-
-            synchronized (mLock) {
-                final int size = mPending.size();
-                final ArrayList<String> result = new ArrayList(size);
-                for (int i = 0; i < size; i++) {
-                    result.add(mPending.get(i).getUri().toString());
-                }
-                return result;
-            }
+            return mPendingReports.getPendingReports();
         }
 
         /**
-         * ONEWAY binder call to mark a report as approved.
+         * SYNCHRONOUS binder call to mark a report as approved.
          */
         @Override
         public void approveReport(String uri) {
@@ -187,32 +100,14 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PendingReportRec rec;
-                synchronized (mLock) {
-                    rec = findAndRemovePendingReportRecLocked(uri);
-                    if (rec == null) {
-                        Log.e(TAG, "confirmApproved: Couldn't find record for uri: " + uri);
-                        return;
-                    }
-                }
-
-                // Re-do the broadcast, so whoever is listening knows the list changed,
-                // in case another one was added in the meantime.
-                sendBroadcast();
-
-                Log.i(TAG, "Approved report: " + uri);
-                try {
-                    rec.listener.onReportApproved();
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "Failed calling back for approval for: " + uri, ex);
-                }
+                mPendingReports.approveReport(uri);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
 
         /**
-         * ONEWAY binder call to mark a report as NOT approved.
+         * SYNCHRONOUS binder call to mark a report as NOT approved.
          */
         @Override
         public void denyReport(String uri) {
@@ -220,25 +115,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PendingReportRec rec;
-                synchronized (mLock) {
-                    rec = findAndRemovePendingReportRecLocked(uri);
-                    if (rec == null) {
-                        Log.e(TAG, "confirmDenied: Couldn't find record for uri: " + uri);
-                        return;
-                    }
-                }
-
-                // Re-do the broadcast, so whoever is listening knows the list changed,
-                // in case another one was added in the meantime.
-                sendBroadcast();
-
-                Log.i(TAG, "Denied report: " + uri);
-                try {
-                    rec.listener.onReportDenied();
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "Failed calling back for denial for: " + uri, ex);
-                }
+                mPendingReports.denyReport(uri);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -252,27 +129,22 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) {
                 return;
             }
-            if (args.length == 0) {
-                // Standard text dumpsys
-                final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-                synchronized (mLock) {
-                    final int size = mPending.size();
-                    writer.println("mPending: (" + size + ")");
-                    for (int i = 0; i < size; i++) {
-                        final PendingReportRec entry = mPending.get(i);
-                        writer.println(String.format("  %11d %s: %s", entry.addedRealtime,
-                                    df.format(new Date(entry.addedWalltime)),
-                                    entry.getUri().toString()));
-                    }
-                }
-            }
+            mPendingReports.dump(fd, writer, args);
         }
 
+        /**
+         * Inside the binder interface class because we want to do all of the authorization
+         * here, before calling out to the helper objects.
+         */
         private void enforceRequestAuthorizationPermission() {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL, null);
         }
 
+        /**
+         * Inside the binder interface class because we want to do all of the authorization
+         * here, before calling out to the helper objects.
+         */
         private void enforceAuthorizePermission() {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.APPROVE_INCIDENT_REPORTS, null);
@@ -285,8 +157,7 @@
      */
     public IncidentCompanionService(Context context) {
         super(context);
-        mPackageManager = context.getPackageManager();
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+        mPendingReports = new PendingReports(context);
     }
 
     /**
@@ -307,239 +178,9 @@
         super.onBootPhase(phase);
         switch (phase) {
             case SystemService.PHASE_BOOT_COMPLETED:
-                // Release the enqueued work.
-                mRequestQueue.start();
+                mPendingReports.onBootCompleted();
                 break;
         }
     }
-
-    /**
-     * Start the confirmation process.
-     */
-    private void authorizeReportImpl(int callingUid, final String callingPackage, int flags,
-            final IIncidentAuthListener listener) {
-        // Enforce that the calling package pertains to the callingUid.
-        if (!isPackageInUid(callingUid, callingPackage)) {
-            Log.w(TAG, "Calling uid " + callingUid + " doesn't match package "
-                    + callingPackage);
-            denyReportBeforeAddingRec(listener, callingPackage);
-            return;
-        }
-
-        // Find the primary user of this device.
-        final int primaryUser = getAndValidateUser();
-        if (primaryUser == UserHandle.USER_NULL) {
-            denyReportBeforeAddingRec(listener, callingPackage);
-            return;
-        }
-
-        // Find the approver app (hint: it's PermissionController).
-        final ComponentName receiver = getApproverComponent(primaryUser);
-        if (receiver == null) {
-            // We couldn't find an approver... so deny the request here and now, before we
-            // do anything else.
-            denyReportBeforeAddingRec(listener, callingPackage);
-            return;
-        }
-
-        // Save the record for when the PermissionController comes back to authorize it.
-        PendingReportRec rec = null;
-        synchronized (mLock) {
-            rec = new PendingReportRec(callingPackage, flags, listener);
-            mPending.add(rec);
-        }
-
-        try {
-            listener.asBinder().linkToDeath(() -> {
-                Log.i(TAG, "Got death notification listener=" + listener);
-                cancelReportImpl(listener, receiver, primaryUser);
-            }, 0);
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri());
-            // First, remove from our list.
-            cancelReportImpl(listener, receiver, primaryUser);
-        }
-
-        // Go tell Permission controller to start asking the user.
-        sendBroadcast(receiver, primaryUser);
-    }
-
-    /**
-     * Cancel a pending report request (because of an explicit call to cancel)
-     */
-    private void cancelReportImpl(IIncidentAuthListener listener) {
-        final int primaryUser = getAndValidateUser();
-        final ComponentName receiver = getApproverComponent(primaryUser);
-        if (primaryUser != UserHandle.USER_NULL && receiver != null) {
-            cancelReportImpl(listener, receiver, primaryUser);
-        }
-    }
-
-    /**
-     * Cancel a pending report request (either because of an explicit call to cancel
-     * by the calling app, or because of a binder death).
-     */
-    private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver,
-            int primaryUser) {
-        // First, remove from our list.
-        synchronized (mLock) {
-            removePendingReportRecLocked(listener);
-        }
-        // Second, call back to PermissionController to say it's canceled.
-        sendBroadcast(receiver, primaryUser);
-    }
-
-    /**
-     * Send an extra copy of the broadcast, to tell them that the list has changed
-     * because of an addition or removal.  This function is less aggressive than
-     * authorizeReportImpl in logging about failures, because this is for use in
-     * cleanup cases to keep the apps' list in sync with ours.
-     */
-    private void sendBroadcast() {
-        final int primaryUser = getAndValidateUser();
-        if (primaryUser == UserHandle.USER_NULL) {
-            return;
-        }
-        final ComponentName receiver = getApproverComponent(primaryUser);
-        if (receiver == null) {
-            return;
-        }
-        sendBroadcast(receiver, primaryUser);
-    }
-
-    /**
-     * Send the confirmation broadcast.
-     */
-    private void sendBroadcast(ComponentName receiver, int primaryUser) {
-        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
-        intent.setComponent(receiver);
-
-        // Send it to the primary user.
-        getContext().sendBroadcastAsUser(intent, UserHandle.getUserHandleForUid(primaryUser),
-                android.Manifest.permission.APPROVE_INCIDENT_REPORTS);
-    }
-
-    /**
-     * Remove a PendingReportRec keyed by uri, and return it.
-     */
-    private PendingReportRec findAndRemovePendingReportRecLocked(String uriString) {
-        final Uri uri = Uri.parse(uriString);
-        final int id;
-        try {
-            final String idStr = uri.getQueryParameter(IncidentManager.URI_PARAM_ID);
-            id = Integer.parseInt(idStr);
-        } catch (NumberFormatException ex) {
-            Log.w(TAG, "Can't parse id from: " + uriString);
-            return null;
-        }
-        final int size = mPending.size();
-        for (int i = 0; i < size; i++) {
-            final PendingReportRec rec = mPending.get(i);
-            if (rec.id == id) {
-                mPending.remove(i);
-                return rec;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Remove a PendingReportRec keyed by listener.
-     */
-    private void removePendingReportRecLocked(IIncidentAuthListener listener) {
-        final int size = mPending.size();
-        for (int i = 0; i < size; i++) {
-            final PendingReportRec rec = mPending.get(i);
-            if (rec.listener.asBinder() == listener.asBinder()) {
-                Log.i(TAG, "  ...Removed PendingReportRec index=" + i + ": " + rec.getUri());
-                mPending.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Just call listener.deny() (wrapping the RemoteException), without try to
-     * add it to the list.
-     */
-    private void denyReportBeforeAddingRec(IIncidentAuthListener listener, String pkg) {
-        try {
-            listener.onReportDenied();
-        } catch (RemoteException ex) {
-            Log.w(TAG, "Failed calling back for denial for " + pkg, ex);
-        }
-    }
-
-    /**
-     * Check whether the current user is the primary user, and return the user id if they are.
-     * Returns UserHandle.USER_NULL if not valid.
-     */
-    private int getAndValidateUser() {
-        // Current user
-        UserInfo currentUser;
-        try {
-            currentUser = ActivityManager.getService().getCurrentUser();
-        } catch (RemoteException ex) {
-            // We're already inside the system process.
-            throw new RuntimeException(ex);
-        }
-
-        // Primary user
-        final UserManager um = UserManager.get(getContext());
-        final UserInfo primaryUser = um.getPrimaryUser();
-
-        // Check that we're using the right user.
-        if (currentUser == null) {
-            Log.w(TAG, "No current user.  Nobody to approve the report."
-                    + " The report will be denied.");
-            return UserHandle.USER_NULL;
-        }
-        if (primaryUser == null) {
-            Log.w(TAG, "No primary user.  Nobody to approve the report."
-                    + " The report will be denied.");
-            return UserHandle.USER_NULL;
-        }
-        if (primaryUser.id != currentUser.id) {
-            Log.w(TAG, "Only the primary user can approve bugreports, but they are not"
-                    + " the current user. The report will be denied.");
-            return UserHandle.USER_NULL;
-        }
-
-        return primaryUser.id;
-    }
-
-    /**
-     * Return the ComponentName of the BroadcastReceiver that will approve reports.
-     * The system must have zero or one of these installed.  We only look on the
-     * system partition.  When the broadcast happens, the component will also need
-     * have the APPROVE_INCIDENT_REPORTS permission.
-     */
-    private ComponentName getApproverComponent(int userId) {
-        // Find the one true BroadcastReceiver
-        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
-        final List<ResolveInfo> matches = mPackageManager.queryBroadcastReceiversAsUser(intent,
-                PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
-        if (matches.size() == 1) {
-            return matches.get(0).getComponentInfo().getComponentName();
-        } else {
-            Log.w(TAG, "Didn't find exactly one BroadcastReceiver to handle "
-                    + Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED
-                    + ". The report will be denied. size="
-                    + matches.size() + ": matches=" + matches);
-            return null;
-        }
-    }
-
-    /**
-     * Return whether the package is one of the packages installed for the uid.
-     */
-    private boolean isPackageInUid(int uid, String packageName) {
-        try {
-            mAppOpsManager.checkPackage(uid, packageName);
-            return true;
-        } catch (SecurityException ex) {
-            return false;
-        }
-    }
 }
 
diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java
new file mode 100644
index 0000000..519ed41
--- /dev/null
+++ b/services/core/java/com/android/server/incident/PendingReports.java
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.incident;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IIncidentAuthListener;
+import android.os.IncidentManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+// TODO: User changes should deny everything that's pending.
+
+/**
+ * Tracker for reports pending approval.
+ */
+class PendingReports {
+    static final String TAG = IncidentCompanionService.TAG;
+
+    private final Handler mHandler = new Handler();
+    private final RequestQueue mRequestQueue = new RequestQueue(mHandler);
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final AppOpsManager mAppOpsManager;
+
+    //
+    // All fields below must be protected by mLock
+    //
+    private final Object mLock = new Object();
+    private final ArrayList<PendingReportRec> mPending = new ArrayList();
+
+    /**
+     * The next ID we'll use when we make a PendingReportRec.
+     */
+    private int mNextPendingId = 1;
+
+    /**
+     * One for each authorization that's pending.
+     */
+    private final class PendingReportRec {
+        public int id;
+        public String callingPackage;
+        public int flags;
+        public IIncidentAuthListener listener;
+        public long addedRealtime;
+        public long addedWalltime;
+
+        /**
+         * Construct a PendingReportRec, with an auto-incremented id.
+         */
+        PendingReportRec(String callingPackage, int flags, IIncidentAuthListener listener) {
+            this.id = mNextPendingId++;
+            this.callingPackage = callingPackage;
+            this.flags = flags;
+            this.listener = listener;
+            this.addedRealtime = SystemClock.elapsedRealtime();
+            this.addedWalltime = System.currentTimeMillis();
+        }
+
+        /**
+         * Get the Uri that contains the flattened data.
+         */
+        Uri getUri() {
+            return (new Uri.Builder())
+                    .scheme(IncidentManager.URI_SCHEME)
+                    .authority(IncidentManager.URI_AUTHORITY)
+                    .path(IncidentManager.URI_PATH)
+                    .appendQueryParameter(IncidentManager.URI_PARAM_ID, Integer.toString(id))
+                    .appendQueryParameter(IncidentManager.URI_PARAM_CALLING_PACKAGE, callingPackage)
+                    .appendQueryParameter(IncidentManager.URI_PARAM_FLAGS, Integer.toString(flags))
+                    .appendQueryParameter(IncidentManager.URI_PARAM_TIMESTAMP,
+                            Long.toString(addedWalltime))
+                    .build();
+        }
+    }
+
+    /**
+     * Construct new PendingReports with the context.
+     */
+    PendingReports(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+    }
+
+    /**
+     * ONEWAY binder call to initiate authorizing the report.  The actual logic is posted
+     * to mRequestQueue, and may happen later.
+     * <p>
+     * The security checks are handled by IncidentCompanionService.
+     */
+    public void authorizeReport(int callingUid, final String callingPackage, final int flags,
+            final IIncidentAuthListener listener) {
+        // Starting the system server is complicated, and rather than try to
+        // have a complicated lifecycle that we share with dumpstated and incidentd,
+        // we will accept the request, and then display it whenever it becomes possible to.
+        mRequestQueue.enqueue(listener.asBinder(), true, () -> {
+            authorizeReportImpl(callingUid, callingPackage, flags, listener);
+        });
+    }
+
+    /**
+     * ONEWAY binder call to cancel the inbound authorization request.
+     * <p>
+     * This is a oneway call, and so is authorizeReport, so the
+     * caller's ordering is preserved.  The other calls on this object are synchronous, so
+     * their ordering is not guaranteed with respect to these calls.  So the implementation
+     * sends out extra broadcasts to allow for eventual consistency.
+     * <p>
+     * The security checks are handled by IncidentCompanionService.
+     */
+    public void cancelAuthorization(final IIncidentAuthListener listener) {
+        mRequestQueue.enqueue(listener.asBinder(), false, () -> {
+            cancelReportImpl(listener);
+        });
+    }
+
+    /**
+     * SYNCHRONOUS binder call to get the list of reports that are pending confirmation
+     * by the user.
+     * <p>
+     * The security checks are handled by IncidentCompanionService.
+     */
+    public List<String> getPendingReports() {
+        synchronized (mLock) {
+            final int size = mPending.size();
+            final ArrayList<String> result = new ArrayList(size);
+            for (int i = 0; i < size; i++) {
+                result.add(mPending.get(i).getUri().toString());
+            }
+            return result;
+        }
+    }
+
+    /**
+     * SYNCHRONOUS binder call to mark a report as approved.
+     * <p>
+     * The security checks are handled by IncidentCompanionService.
+     */
+    public void approveReport(String uri) {
+        final PendingReportRec rec;
+        synchronized (mLock) {
+            rec = findAndRemovePendingReportRecLocked(uri);
+            if (rec == null) {
+                Log.e(TAG, "confirmApproved: Couldn't find record for uri: " + uri);
+                return;
+            }
+        }
+
+        // Re-do the broadcast, so whoever is listening knows the list changed,
+        // in case another one was added in the meantime.
+        sendBroadcast();
+
+        Log.i(TAG, "Approved report: " + uri);
+        try {
+            rec.listener.onReportApproved();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Failed calling back for approval for: " + uri, ex);
+        }
+    }
+
+    /**
+     * SYNCHRONOUS binder call to mark a report as NOT approved.
+     */
+    public void denyReport(String uri) {
+        final PendingReportRec rec;
+        synchronized (mLock) {
+            rec = findAndRemovePendingReportRecLocked(uri);
+            if (rec == null) {
+                Log.e(TAG, "confirmDenied: Couldn't find record for uri: " + uri);
+                return;
+            }
+        }
+
+        // Re-do the broadcast, so whoever is listening knows the list changed,
+        // in case another one was added in the meantime.
+        sendBroadcast();
+
+        Log.i(TAG, "Denied report: " + uri);
+        try {
+            rec.listener.onReportDenied();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Failed calling back for denial for: " + uri, ex);
+        }
+    }
+
+    /**
+     * Implementation of adb shell dumpsys debugreportcompanion.
+     */
+    protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+        if (args.length == 0) {
+            // Standard text dumpsys
+            final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+            synchronized (mLock) {
+                final int size = mPending.size();
+                writer.println("mPending: (" + size + ")");
+                for (int i = 0; i < size; i++) {
+                    final PendingReportRec entry = mPending.get(i);
+                    writer.println(String.format("  %11d %s: %s", entry.addedRealtime,
+                                df.format(new Date(entry.addedWalltime)),
+                                entry.getUri().toString()));
+                }
+            }
+        }
+    }
+
+    /**
+     * Handle the boot process... Starts everything running once the system is
+     * up enough for us to do UI.
+     */
+    public void onBootCompleted() {
+        // Release the enqueued work.
+        mRequestQueue.start();
+    }
+
+    /**
+     * Start the confirmation process.
+     */
+    private void authorizeReportImpl(int callingUid, final String callingPackage, int flags,
+            final IIncidentAuthListener listener) {
+        // Enforce that the calling package pertains to the callingUid.
+        if (!isPackageInUid(callingUid, callingPackage)) {
+            Log.w(TAG, "Calling uid " + callingUid + " doesn't match package "
+                    + callingPackage);
+            denyReportBeforeAddingRec(listener, callingPackage);
+            return;
+        }
+
+        // Find the primary user of this device.
+        final int primaryUser = getAndValidateUser();
+        if (primaryUser == UserHandle.USER_NULL) {
+            denyReportBeforeAddingRec(listener, callingPackage);
+            return;
+        }
+
+        // Find the approver app (hint: it's PermissionController).
+        final ComponentName receiver = getApproverComponent(primaryUser);
+        if (receiver == null) {
+            // We couldn't find an approver... so deny the request here and now, before we
+            // do anything else.
+            denyReportBeforeAddingRec(listener, callingPackage);
+            return;
+        }
+
+        // Save the record for when the PermissionController comes back to authorize it.
+        PendingReportRec rec = null;
+        synchronized (mLock) {
+            rec = new PendingReportRec(callingPackage, flags, listener);
+            mPending.add(rec);
+        }
+
+        try {
+            listener.asBinder().linkToDeath(() -> {
+                Log.i(TAG, "Got death notification listener=" + listener);
+                cancelReportImpl(listener, receiver, primaryUser);
+            }, 0);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri());
+            // First, remove from our list.
+            cancelReportImpl(listener, receiver, primaryUser);
+        }
+
+        // Go tell Permission controller to start asking the user.
+        sendBroadcast(receiver, primaryUser);
+    }
+
+    /**
+     * Cancel a pending report request (because of an explicit call to cancel)
+     */
+    private void cancelReportImpl(IIncidentAuthListener listener) {
+        final int primaryUser = getAndValidateUser();
+        final ComponentName receiver = getApproverComponent(primaryUser);
+        if (primaryUser != UserHandle.USER_NULL && receiver != null) {
+            cancelReportImpl(listener, receiver, primaryUser);
+        }
+    }
+
+    /**
+     * Cancel a pending report request (either because of an explicit call to cancel
+     * by the calling app, or because of a binder death).
+     */
+    private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver,
+            int primaryUser) {
+        // First, remove from our list.
+        synchronized (mLock) {
+            removePendingReportRecLocked(listener);
+        }
+        // Second, call back to PermissionController to say it's canceled.
+        sendBroadcast(receiver, primaryUser);
+    }
+
+    /**
+     * Send an extra copy of the broadcast, to tell them that the list has changed
+     * because of an addition or removal.  This function is less aggressive than
+     * authorizeReportImpl in logging about failures, because this is for use in
+     * cleanup cases to keep the apps' list in sync with ours.
+     */
+    private void sendBroadcast() {
+        final int primaryUser = getAndValidateUser();
+        if (primaryUser == UserHandle.USER_NULL) {
+            return;
+        }
+        final ComponentName receiver = getApproverComponent(primaryUser);
+        if (receiver == null) {
+            return;
+        }
+        sendBroadcast(receiver, primaryUser);
+    }
+
+    /**
+     * Send the confirmation broadcast.
+     */
+    private void sendBroadcast(ComponentName receiver, int primaryUser) {
+        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
+        intent.setComponent(receiver);
+
+        // Send it to the primary user.
+        mContext.sendBroadcastAsUser(intent, UserHandle.getUserHandleForUid(primaryUser),
+                android.Manifest.permission.APPROVE_INCIDENT_REPORTS);
+    }
+
+    /**
+     * Remove a PendingReportRec keyed by uri, and return it.
+     */
+    private PendingReportRec findAndRemovePendingReportRecLocked(String uriString) {
+        final Uri uri = Uri.parse(uriString);
+        final int id;
+        try {
+            final String idStr = uri.getQueryParameter(IncidentManager.URI_PARAM_ID);
+            id = Integer.parseInt(idStr);
+        } catch (NumberFormatException ex) {
+            Log.w(TAG, "Can't parse id from: " + uriString);
+            return null;
+        }
+        final int size = mPending.size();
+        for (int i = 0; i < size; i++) {
+            final PendingReportRec rec = mPending.get(i);
+            if (rec.id == id) {
+                mPending.remove(i);
+                return rec;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Remove a PendingReportRec keyed by listener.
+     */
+    private void removePendingReportRecLocked(IIncidentAuthListener listener) {
+        final int size = mPending.size();
+        for (int i = 0; i < size; i++) {
+            final PendingReportRec rec = mPending.get(i);
+            if (rec.listener.asBinder() == listener.asBinder()) {
+                Log.i(TAG, "  ...Removed PendingReportRec index=" + i + ": " + rec.getUri());
+                mPending.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Just call listener.deny() (wrapping the RemoteException), without try to
+     * add it to the list.
+     */
+    private void denyReportBeforeAddingRec(IIncidentAuthListener listener, String pkg) {
+        try {
+            listener.onReportDenied();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Failed calling back for denial for " + pkg, ex);
+        }
+    }
+
+    /**
+     * Check whether the current user is the primary user, and return the user id if they are.
+     * Returns UserHandle.USER_NULL if not valid.
+     */
+    private int getAndValidateUser() {
+        // Current user
+        UserInfo currentUser;
+        try {
+            currentUser = ActivityManager.getService().getCurrentUser();
+        } catch (RemoteException ex) {
+            // We're already inside the system process.
+            throw new RuntimeException(ex);
+        }
+
+        // Primary user
+        final UserManager um = UserManager.get(mContext);
+        final UserInfo primaryUser = um.getPrimaryUser();
+
+        // Check that we're using the right user.
+        if (currentUser == null) {
+            Log.w(TAG, "No current user.  Nobody to approve the report."
+                    + " The report will be denied.");
+            return UserHandle.USER_NULL;
+        }
+        if (primaryUser == null) {
+            Log.w(TAG, "No primary user.  Nobody to approve the report."
+                    + " The report will be denied.");
+            return UserHandle.USER_NULL;
+        }
+        if (primaryUser.id != currentUser.id) {
+            Log.w(TAG, "Only the primary user can approve bugreports, but they are not"
+                    + " the current user. The report will be denied.");
+            return UserHandle.USER_NULL;
+        }
+
+        return primaryUser.id;
+    }
+
+    /**
+     * Return the ComponentName of the BroadcastReceiver that will approve reports.
+     * The system must have zero or one of these installed.  We only look on the
+     * system partition.  When the broadcast happens, the component will also need
+     * have the APPROVE_INCIDENT_REPORTS permission.
+     */
+    private ComponentName getApproverComponent(int userId) {
+        // Find the one true BroadcastReceiver
+        final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
+        final List<ResolveInfo> matches = mPackageManager.queryBroadcastReceiversAsUser(intent,
+                PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+        if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().getComponentName();
+        } else {
+            Log.w(TAG, "Didn't find exactly one BroadcastReceiver to handle "
+                    + Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED
+                    + ". The report will be denied. size="
+                    + matches.size() + ": matches=" + matches);
+            return null;
+        }
+    }
+
+    /**
+     * Return whether the package is one of the packages installed for the uid.
+     */
+    private boolean isPackageInUid(int uid, String packageName) {
+        try {
+            mAppOpsManager.checkPackage(uid, packageName);
+            return true;
+        } catch (SecurityException ex) {
+            return false;
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 22fabb2..80ab790 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -24,7 +24,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.os.BackgroundThread;
+import com.android.server.FgThread;
 import com.android.server.ServiceWatcher;
 
 /**
@@ -84,7 +84,7 @@
                 overlaySwitchResId,
                 defaultServicePackageNameResId,
                 initialPackageNameResId,
-                BackgroundThread.getHandler()) {
+                FgThread.getHandler()) {
             @Override
             protected void onBind() {
                 runOnBinder(ActivityRecognitionProxy.this::initializeService);
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index e6f0ed9..d9602b8 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -21,7 +21,7 @@
 import android.location.GeocoderParams;
 import android.location.IGeocodeProvider;
 
-import com.android.internal.os.BackgroundThread;
+import com.android.server.FgThread;
 import com.android.server.ServiceWatcher;
 
 import java.util.List;
@@ -53,7 +53,7 @@
             int initialPackageNamesResId) {
         mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
-                BackgroundThread.getHandler());
+                FgThread.getHandler());
     }
 
     private boolean bind() {
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index 38c7d49..ce93661 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -30,7 +30,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.internal.os.BackgroundThread;
+import com.android.server.FgThread;
 import com.android.server.ServiceWatcher;
 
 /**
@@ -82,7 +82,7 @@
         mContext = context;
         mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
-                BackgroundThread.getHandler()) {
+                FgThread.getHandler()) {
             @Override
             protected void onBind() {
                 runOnBinder(mUpdateGeofenceHardware);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d346ddc..ab9f711 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -332,9 +332,6 @@
     // true if we started navigation
     private boolean mStarted;
 
-    // true if single shot request is in progress
-    private boolean mSingleShot;
-
     // capabilities of the GPS engine
     private int mEngineCapabilities;
 
@@ -455,7 +452,7 @@
 
             switch (action) {
                 case ALARM_WAKEUP:
-                    startNavigating(false);
+                    startNavigating();
                     break;
                 case ALARM_TIMEOUT:
                     hibernate();
@@ -852,10 +849,9 @@
      * allowed mode from properties.
      *
      * @param agpsEnabled whether AGPS is enabled by settings value
-     * @param singleShot  whether "singleshot" is needed
      * @return SUPL mode (MSA vs MSB vs STANDALONE)
      */
-    private int getSuplMode(boolean agpsEnabled, boolean singleShot) {
+    private int getSuplMode(boolean agpsEnabled) {
         if (agpsEnabled) {
             int suplMode = mGnssConfiguration.getSuplMode(0);
             if (suplMode == 0) {
@@ -867,14 +863,6 @@
             if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
                 return GPS_POSITION_MODE_MS_BASED;
             }
-            // for now, just as the legacy code did, we fallback to MS-Assisted if it is available,
-            // do fallback only for single-shot requests, because it is too expensive to do for
-            // periodic requests as well
-            if (singleShot
-                    && hasCapability(GPS_CAPABILITY_MSA)
-                    && (suplMode & AGPS_SUPL_MODE_MSA) != 0) {
-                return GPS_POSITION_MODE_MS_ASSISTED;
-            }
         }
         return GPS_POSITION_MODE_STANDALONE;
     }
@@ -965,22 +953,6 @@
             return;
         }
 
-        boolean singleShot = false;
-
-        // see if the request is for a single update
-        if (mProviderRequest.locationRequests != null
-                && mProviderRequest.locationRequests.size() > 0) {
-            // if any request has zero or more than one updates
-            // requested, then this is not single-shot mode
-            singleShot = true;
-
-            for (LocationRequest lr : mProviderRequest.locationRequests) {
-                if (lr.getNumUpdates() != 1) {
-                    singleShot = false;
-                }
-            }
-        }
-
         if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
         if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) {
             // update client uids
@@ -1003,7 +975,7 @@
                 }
             } else if (!mStarted) {
                 // start GPS
-                startNavigating(singleShot);
+                startNavigating();
             } else {
                 // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
                 mAlarmManager.cancel(mTimeoutIntent);
@@ -1148,13 +1120,12 @@
         }
     }
 
-    private void startNavigating(boolean singleShot) {
+    private void startNavigating() {
         if (!mStarted) {
-            if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot);
+            if (DEBUG) Log.d(TAG, "startNavigating");
             mTimeToFirstFix = 0;
             mLastFixTime = 0;
             mStarted = true;
-            mSingleShot = singleShot;
             mPositionMode = GPS_POSITION_MODE_STANDALONE;
             // Notify about suppressed output, if speed limit was previously exceeded.
             // Elsewhere, we check again with every speed output reported.
@@ -1166,7 +1137,7 @@
             boolean agpsEnabled =
                     (Settings.Global.getInt(mContext.getContentResolver(),
                             Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
-            mPositionMode = getSuplMode(agpsEnabled, singleShot);
+            mPositionMode = getSuplMode(agpsEnabled);
 
             if (DEBUG) {
                 String mode;
@@ -1221,7 +1192,6 @@
         if (DEBUG) Log.d(TAG, "stopNavigating");
         if (mStarted) {
             mStarted = false;
-            mSingleShot = false;
             native_stop();
             mLastFixTime = 0;
             // native_stop() may reset the position mode in hardware.
@@ -1300,10 +1270,6 @@
             mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
         }
 
-        if (mSingleShot) {
-            stopNavigating();
-        }
-
         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
             // For devices that use framework scheduling, a timer may be set to ensure we don't
             // spend too much power searching for a location, when the requested update rate is
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 6b5b1be..776beb5 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -31,8 +31,8 @@
 import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.TransferPipe;
+import com.android.server.FgThread;
 import com.android.server.LocationManagerService;
 import com.android.server.ServiceWatcher;
 
@@ -105,7 +105,7 @@
 
         mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
-                BackgroundThread.getHandler()) {
+                FgThread.getHandler()) {
 
             @Override
             protected void onBind() {
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
index 409060e..5c73178 100644
--- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -42,6 +42,8 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
@@ -57,6 +59,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
@@ -1011,50 +1014,17 @@
                 if (DEBUG) {
                     Log.d(TAG, "Session2 is created " + sessionToken);
                 }
-                if (pid != sessionToken.getPid()) {
-                    throw new SecurityException("Unexpected Session2Token's PID, expected=" + pid
-                            + " but actually=" + sessionToken.getPid());
-                }
                 if (uid != sessionToken.getUid()) {
                     throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                             + " but actually=" + sessionToken.getUid());
                 }
-                int userId = UserHandle.getUserId(uid);
-                List<Session2Token> session2Tokens = mSession2TokensPerUser.get(userId);
-                if (session2Tokens.contains(sessionToken)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "notifySession2Created(): Ignoring already existing token "
-                                + sessionToken);
-                    }
-                    return;
-                }
-                session2Tokens.add(sessionToken);
-                pushSession2TokensChangedLocked(userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifySession2Destroyed(Session2Token sessionToken) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Session2 is destroyed " + sessionToken);
-                }
-                if (pid != sessionToken.getPid()) {
-                    throw new SecurityException("Unexpected Session2Token's PID, expected=" + pid
-                            + " but actually=" + sessionToken.getPid());
-                }
-                if (uid != sessionToken.getUid()) {
-                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
-                            + " but actually=" + sessionToken.getUid());
-                }
-                int userId = UserHandle.getUserId(uid);
-                mSession2TokensPerUser.get(userId).remove(sessionToken);
-                pushSession2TokensChangedLocked(userId);
+                Controller2Callback callback = new Controller2Callback(sessionToken);
+                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
+                //       it's closed.
+                // TODO: Keep controller as well for better readability
+                //       because the GC behavior isn't straightforward.
+                MediaController2 controller = new MediaController2(mContext, sessionToken,
+                        new HandlerExecutor(mHandler), callback);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2235,4 +2205,30 @@
             obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
         }
     }
+
+    private class Controller2Callback extends MediaController2.ControllerCallback {
+        private final Session2Token mToken;
+
+        Controller2Callback(Session2Token token) {
+            mToken = token;
+        }
+
+        @Override
+        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).add(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).remove(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 1cbf0bf..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -270,9 +270,7 @@
             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
         }
 
-        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
-            mListener.onOverlaysChanged(packageName, userId);
-        }
+        updateAllOverlaysForTarget(packageName, userId, 0);
     }
 
     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c2ac27a..62c4815 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -34,9 +34,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -56,6 +58,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
@@ -153,6 +156,8 @@
 
         private final Object mVouchedSignaturesLocked = new Object();
 
+        private PackageInstallerService mPackageInstallerService;
+
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -204,8 +209,7 @@
         }
 
         /*
-         * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
-         *          android.content.pm.IOnAppsChangedListener)
+         * @see android.content.pm.ILauncherApps#addOnAppsChangedListener
          */
         @Override
         public void addOnAppsChangedListener(String callingPackage, IOnAppsChangedListener listener)
@@ -228,8 +232,7 @@
         }
 
         /*
-         * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
-         *          android.content.pm.IOnAppsChangedListener)
+         * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener
          */
         @Override
         public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
@@ -246,6 +249,44 @@
         }
 
         /**
+         * @see android.content.pm.ILauncherApps#registerPackageInstallerCallback
+         */
+        @Override
+        public void registerPackageInstallerCallback(String callingPackage,
+                IPackageInstallerCallback callback) {
+            verifyCallingPackage(callingPackage);
+            UserHandle callingIdUserHandle = new UserHandle(getCallingUserId());
+            getPackageInstallerService().registerCallback(callback, eventUserId ->
+                            isEnabledProfileOf(callingIdUserHandle,
+                                    new UserHandle(eventUserId), "shouldReceiveEvent"));
+        }
+
+        @Override
+        public ParceledListSlice<SessionInfo> getAllSessions(String callingPackage) {
+            verifyCallingPackage(callingPackage);
+            List<SessionInfo> sessionInfos = new ArrayList<>();
+            int[] userIds = mUm.getEnabledProfileIds(getCallingUserId());
+            long token = Binder.clearCallingIdentity();
+            try {
+                for (int userId : userIds) {
+                    sessionInfos.addAll(getPackageInstallerService().getAllSessions(userId)
+                            .getList());
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return new ParceledListSlice<>(sessionInfos);
+        }
+
+        private PackageInstallerService getPackageInstallerService() {
+            if (mPackageInstallerService == null) {
+                mPackageInstallerService = ((PackageInstallerService) ((PackageManagerService)
+                        ServiceManager.getService("package")).getPackageInstaller());
+            }
+            return mPackageInstallerService;
+        }
+
+        /**
          * Register a receiver to watch for package broadcasts
          */
         private void startWatchingPackageBroadcasts() {
@@ -430,6 +471,9 @@
             if (!mVouchedSignaturesByUser.containsKey(user)) {
                 initVouchedSignatures(user);
             }
+            if (isManagedProfileAdmin(user, appInfo.packageName)) {
+                return false;
+            }
             if (mVouchProviders.contains(appInfo.packageName)) {
                 // If it's a vouching packages then we must show hidden app
                 return true;
@@ -453,6 +497,24 @@
             return true;
         }
 
+        private boolean isManagedProfileAdmin(UserHandle user, String packageName) {
+            final List<UserInfo> userInfoList = mUm.getProfiles(user.getIdentifier());
+            for (int i = 0; i < userInfoList.size(); i++) {
+                UserInfo userInfo = userInfoList.get(i);
+                if (!userInfo.isManagedProfile()) {
+                    continue;
+                }
+                ComponentName componentName = mDpm.getProfileOwnerAsUser(userInfo.getUserHandle());
+                if (componentName == null) {
+                    continue;
+                }
+                if (componentName.getPackageName().equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         @VisibleForTesting
         static String computePackageCertDigest(Signature signature) {
             MessageDigest messageDigest;
@@ -848,6 +910,29 @@
         }
 
         @Override
+        public void startSessionDetailsActivityAsUser(IApplicationThread caller,
+                String callingPackage, SessionInfo sessionInfo, Rect sourceBounds,
+                Bundle opts, UserHandle userHandle) throws RemoteException {
+            int userId = userHandle.getIdentifier();
+            if (!canAccessProfile(userId, "Cannot start details activity")) {
+                return;
+            }
+
+            Intent i = new Intent(Intent.ACTION_VIEW)
+                    .setData(new Uri.Builder()
+                            .scheme("market")
+                            .authority("details")
+                            .appendQueryParameter("id", sessionInfo.appPackageName)
+                            .build())
+                    .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
+                            .authority(callingPackage).build());
+            i.setSourceBounds(sourceBounds);
+
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, i, opts,
+                    userId);
+        }
+
+        @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
                 ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 642bfa2..e7dace0 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -138,7 +138,7 @@
                 // to dealing with this as we'll now have to listen to all config changes and
                 // regenerate the data if required. Also, is this the right way to parse a resource
                 // reference out of an XML file ?
-                final String moduleName = packageResources.getString(
+                final CharSequence moduleName = packageResources.getText(
                         Integer.parseInt(parser.getAttributeValue(null, "name").substring(1)));
                 final String modulePackageName = XmlUtils.readStringAttribute(parser,
                         "packageName");
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 60d7925..33b8641 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -51,6 +51,8 @@
 per-file UserManagerService.java = yamasani@google.com
 per-file UserRestrictionsUtils.java = omakoto@google.com
 per-file UserRestrictionsUtils.java = yamasani@google.com
+per-file UserRestrictionsUtils.java = rubinxu@google.com
+per-file UserRestrictionsUtils.java = sandness@google.com
 
 # security
 per-file KeySetHandle.java = cbrubaker@google.com
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 94b1b36..5b38208 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -507,7 +507,7 @@
         int flags = info.flags;
         boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
         // When an app or priv app is configured to run out of box, only verify it.
-        if (info.isCodeIntegrityPreferred()
+        if (info.isEmbeddedDexUsed()
                 || (info.isPrivilegedApp()
                     && DexManager.isPackageSelectedToRunOob(info.packageName))) {
             return "verify";
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 146a2f3..af50c87 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -59,7 +59,6 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
@@ -107,6 +106,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Random;
+import java.util.function.IntPredicate;
 
 /** The service responsible for installing packages. */
 public class PackageInstallerService extends IPackageInstaller.Stub implements
@@ -204,13 +204,17 @@
         mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
         mSessionsDir.mkdirs();
 
-        mStagingManager = new StagingManager(pm);
+        mStagingManager = new StagingManager(pm, this);
     }
 
     private void setBootCompleted()  {
         mBootCompleted = true;
     }
 
+    boolean isBootCompleted()  {
+        return mBootCompleted;
+    }
+
     public void systemReady() {
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
@@ -245,13 +249,24 @@
             // the updated information.
             writeSessionsLocked();
 
+        }
+    }
+
+    void restoreAndApplyStagedSessionIfNeeded() {
+        List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
+        synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
                 if (session.isStaged()) {
-                    mStagingManager.restoreSession(session);
+                    stagedSessionsToRestore.add(session);
                 }
             }
         }
+        // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
+        // atomic install which needs to query sessions, which requires lock on mSessions.
+        for (PackageInstallerSession session : stagedSessionsToRestore) {
+            mStagingManager.restoreSession(session);
+        }
     }
 
     @GuardedBy("mSessions")
@@ -804,7 +819,14 @@
     public void registerCallback(IPackageInstallerCallback callback, int userId) {
         mPermissionManager.enforceCrossUserPermission(
                 Binder.getCallingUid(), userId, true, false, "registerCallback");
-        mCallbacks.register(callback, userId);
+        registerCallback(callback, eventUserId -> userId == eventUserId);
+    }
+
+    /**
+     * Assume permissions already checked and caller's identity cleared
+     */
+    public void registerCallback(IPackageInstallerCallback callback, IntPredicate userCheck) {
+        mCallbacks.register(callback, userCheck);
     }
 
     @Override
@@ -1026,8 +1048,8 @@
             super(looper);
         }
 
-        public void register(IPackageInstallerCallback callback, int userId) {
-            mCallbacks.register(callback, new UserHandle(userId));
+        public void register(IPackageInstallerCallback callback, IntPredicate userCheck) {
+            mCallbacks.register(callback, userCheck);
         }
 
         public void unregister(IPackageInstallerCallback callback) {
@@ -1040,9 +1062,8 @@
             final int n = mCallbacks.beginBroadcast();
             for (int i = 0; i < n; i++) {
                 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
-                final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
-                // TODO: dispatch notifications for slave profiles
-                if (userId == user.getIdentifier()) {
+                final IntPredicate userCheck = (IntPredicate) mCallbacks.getBroadcastCookie(i);
+                if (userCheck.test(userId)) {
                     try {
                         invokeCallback(callback, msg);
                     } catch (RemoteException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 494ec3f..995f335 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -473,6 +473,7 @@
         final SessionInfo info = new SessionInfo();
         synchronized (mLock) {
             info.sessionId = sessionId;
+            info.userId = userId;
             info.installerPackageName = mInstallerPackageName;
             info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
                     mResolvedBaseFile.getAbsolutePath() : null;
@@ -1000,10 +1001,6 @@
         // cannot be modified anymore, there is no leak of information. For staged sessions,
         // further validation is performed by the staging manager.
         if (!params.isMultiPackage) {
-            if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
-                // For APEX, validation is done by StagingManager post-commit.
-                return;
-            }
             final PackageInfo pkgInfo = mPm.getPackageInfo(
                     params.appPackageName, PackageManager.GET_SIGNATURES
                             | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
@@ -1011,7 +1008,11 @@
             resolveStageDirLocked();
 
             try {
-                validateApkInstallLocked(pkgInfo);
+                if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                    validateApexInstallLocked();
+                } else {
+                    validateApkInstallLocked(pkgInfo);
+                }
             } catch (PackageManagerException e) {
                 throw e;
             } catch (Throwable e) {
@@ -1303,6 +1304,27 @@
     }
 
     /**
+     * Validate apex install.
+     * <p>
+     * Sets {@link #mResolvedBaseFile} for RollbackManager to use.
+     */
+    @GuardedBy("mLock")
+    private void validateApexInstallLocked()
+            throws PackageManagerException {
+        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+        if (ArrayUtils.isEmpty(addedFiles)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+        }
+
+        if (ArrayUtils.size(addedFiles) > 1) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Too many files for apex install");
+        }
+
+        mResolvedBaseFile = addedFiles[0];
+    }
+
+    /**
      * Validate install by confirming that all application packages are have
      * consistent package name, version code, and signing certificates.
      * <p>
@@ -1562,12 +1584,12 @@
                 }
             }
         }
-        if (baseApk.preferCodeIntegrity) {
+        if (baseApk.useEmbeddedDex) {
             for (File file : mResolvedStagedFiles) {
                 if (file.getName().endsWith(".apk")
-                        && !DexManager.auditUncompressedCodeInApk(file.getPath())) {
+                        && !DexManager.auditUncompressedDexInApk(file.getPath())) {
                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                            "Some code are not uncompressed and aligned correctly for "
+                            "Some dex are not uncompressed and aligned correctly for "
                             + mPackageName);
                 }
             }
@@ -1663,6 +1685,12 @@
         }
     }
 
+    String getInstallerPackageName() {
+        synchronized (mLock) {
+            return mInstallerPackageName;
+        }
+    }
+
     private static String getRelativePath(File file, File base) throws IOException {
         final String pathStr = file.getAbsolutePath();
         final String baseStr = base.getAbsolutePath();
@@ -1963,7 +1991,7 @@
 
         // Send broadcast to default launcher only if it's a new install
         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
-        if (success && isNewInstall) {
+        if (success && isNewInstall && mPm.mInstallerService.isBootCompleted()) {
             mPm.sendSessionCommitBroadcast(generateInfo(), userId);
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2547d9..957fc4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3967,6 +3967,7 @@
                     }
                     return generatePackageInfo(ps, flags, userId);
                 }
+                // TODO(b/123680735): support MATCH_APEX|MATCH_FACTORY_ONLY case
             }
 
             PackageParser.Package p = mPackages.get(packageName);
@@ -3996,6 +3997,30 @@
                 }
                 return generatePackageInfo(ps, flags, userId);
             }
+            //
+            if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) {
+                //TODO(b/123052859) Don't do file operations every time there is a query.
+                final IApexService apex = IApexService.Stub.asInterface(
+                        ServiceManager.getService("apexservice"));
+                if (apex != null) {
+                    try {
+                        final ApexInfo activePkg = apex.getActivePackage(packageName);
+                        if (activePkg != null) {
+                            try {
+                                return PackageParser.generatePackageInfoFromApex(
+                                        new File(activePkg.packagePath), true /* collect certs */);
+                            } catch (PackageParserException pe) {
+                                throw new IllegalStateException("Unable to parse: " + activePkg,
+                                        pe);
+                            }
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());
+                    }
+                } else {
+                    Log.e(TAG, "Unable to connect to apexservice for querying packages.");
+                }
+            }
         }
         return null;
     }
@@ -7889,6 +7914,8 @@
                 }
             }
             if (listApex) {
+                // TODO(b/119767311): include uninstalled/inactive APEX if
+                //  MATCH_UNINSTALLED_PACKAGES is set.
                 final IApexService apex = IApexService.Stub.asInterface(
                         ServiceManager.getService("apexservice"));
                 if (apex != null) {
@@ -13883,6 +13910,9 @@
 
             final String packageName = res.pkg.applicationInfo.packageName;
             final String seInfo = res.pkg.applicationInfo.seInfo;
+            final int[] allUsers = sUserManager.getUserIds();
+            final int[] installedUsers;
+
             final PackageSetting ps;
             int appId = -1;
             long ceDataInode = -1;
@@ -13892,11 +13922,16 @@
                     appId = ps.appId;
                     ceDataInode = ps.getCeDataInode(userId);
                 }
+
+                // NOTE: We ignore the user specified in the InstallParam because we know this is
+                // an update, and hence need to restore data for all installed users.
+                installedUsers = ps.queryInstalledUsers(allUsers, true);
             }
 
             if (ps != null) {
                 try {
-                    rm.restoreUserData(packageName, userId, appId, ceDataInode, seInfo, token);
+                    rm.restoreUserData(packageName, installedUsers, appId, ceDataInode,
+                            seInfo, token);
                 } catch (RemoteException re) {
                     // Cannot happen, the RollbackManager is hosted in the same process.
                 }
@@ -14593,6 +14628,17 @@
                             TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
                     mPendingEnableRollback.append(enableRollbackToken, this);
 
+                    final int[] installedUsers;
+                    synchronized (mPackages) {
+                        PackageSetting ps = mSettings.getPackageLPr(pkgLite.packageName);
+                        if (ps != null) {
+                            installedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(),
+                                    true);
+                        } else {
+                            installedUsers = new int[0];
+                        }
+                    }
+
                     // TODO(ruhler) b/112431924: What user? Test for multi-user.
                     Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
                     enableRollbackIntent.putExtra(
@@ -14603,7 +14649,7 @@
                             installFlags);
                     enableRollbackIntent.putExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS,
-                            resolveUserIds(args.user.getIdentifier()));
+                            installedUsers);
                     enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
                             PACKAGE_MIME_TYPE);
                     enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -20082,7 +20128,8 @@
         return mContext.getString(R.string.config_defaultWellbeingPackage);
     }
 
-    private String getAppPredictionServicePackageName() {
+    @Override
+    public String getAppPredictionServicePackageName() {
         String flattenedAppPredictionServiceComponentName =
                 mContext.getString(R.string.config_defaultAppPredictionService);
         if (flattenedAppPredictionServiceComponentName == null) {
@@ -20733,6 +20780,11 @@
         }
 
         mModuleInfoProvider.systemReady();
+
+        // Installer service might attempt to install some packages that have been staged for
+        // installation on reboot. Make sure this is the last component to be call since the
+        // installation might require other components to be ready.
+        mInstallerService.restoreAndApplyStagedSessionIfNeeded();
     }
 
     public void waitForAppDataPrepared() {
@@ -23283,6 +23335,8 @@
                     return mConfiguratorPackage;
                 case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
                     return mIncidentReportApproverPackage;
+                case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
+                    return mAppPredictionServicePackage;
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c4d27e5..84fd01a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -21,6 +21,10 @@
 import android.apex.ApexInfoList;
 import android.apex.ApexSessionInfo;
 import android.apex.IApexService;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
@@ -29,7 +33,10 @@
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.Signature;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
@@ -40,9 +47,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -53,14 +64,16 @@
 
     private static final String TAG = "StagingManager";
 
+    private final PackageInstallerService mPi;
     private final PackageManagerService mPm;
     private final Handler mBgHandler;
 
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
 
-    StagingManager(PackageManagerService pm) {
+    StagingManager(PackageManagerService pm, PackageInstallerService pi) {
         mPm = pm;
+        mPi = pi;
         mBgHandler = BackgroundThread.getHandler();
     }
 
@@ -85,7 +98,7 @@
         return new ParceledListSlice<>(result);
     }
 
-    private static boolean validateApexSignatureLocked(String apexPath, String packageName) {
+    private static boolean validateApexSignature(String apexPath, String packageName) {
         final SigningDetails signingDetails;
         try {
             signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
@@ -173,6 +186,13 @@
     private void preRebootVerification(@NonNull PackageInstallerSession session) {
         boolean success = true;
 
+        if (!sessionContainsApex(session)) {
+            // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+            // right away.
+            session.setStagedSessionReady();
+            return;
+        }
+
         final ApexInfoList apexInfoList = new ApexInfoList();
         // APEX checks. For single-package sessions, check if they contain an APEX. For
         // multi-package sessions, find all the child sessions that contain an APEX.
@@ -199,16 +219,13 @@
                     "APEX staging failed, check logcat messages from apexd for more details.");
         }
 
-        if (apexInfoList.apexInfos.length > 0) {
+        if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) {
             // For APEXes, we validate the signature here before we mark the session as ready,
             // so we fail the session early if there is a signature mismatch. For APKs, the
             // signature verification will be done by the package manager at the point at which
             // it applies the staged install.
-            //
-            // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
-            // right away.
             for (ApexInfo apexPackage : apexInfoList.apexInfos) {
-                if (!validateApexSignatureLocked(apexPackage.packagePath,
+                if (!validateApexSignature(apexPackage.packagePath,
                         apexPackage.packageName)) {
                     session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED,
                             "APK-container signature verification failed for package "
@@ -229,35 +246,182 @@
         }
     }
 
+    private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
+        if (!session.isMultiPackage()) {
+            return isApexSession(session);
+        }
+        synchronized (mStagedSessions) {
+            return !(Arrays.stream(session.getChildSessionIds())
+                    // Retrieve cached sessions matching ids.
+                    .mapToObj(i -> mStagedSessions.get(i))
+                    // Filter only the ones containing APEX.
+                    .filter(childSession -> isApexSession(childSession))
+                    .collect(Collectors.toList())
+                    .isEmpty());
+        }
+    }
+
     private void resumeSession(@NonNull PackageInstallerSession session) {
-        // Check with apexservice whether the apex
-        // packages have been activated.
-        final IApexService apex = IApexService.Stub.asInterface(
-                ServiceManager.getService("apexservice"));
-        ApexSessionInfo apexSessionInfo;
-        try {
-            apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to contact apexservice", re);
-            // TODO should we retry here? Mark the session as failed?
+        if (sessionContainsApex(session)) {
+            // Check with apexservice whether the apex
+            // packages have been activated.
+            final IApexService apex = IApexService.Stub.asInterface(
+                    ServiceManager.getService("apexservice"));
+            ApexSessionInfo apexSessionInfo;
+            try {
+                apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to contact apexservice", re);
+                // TODO should we retry here? Mark the session as failed?
+                return;
+            }
+            if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
+                session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
+                        "APEX activation failed. Check logcat messages from apexd for "
+                                + "more information.");
+                return;
+            }
+            if (apexSessionInfo.isVerified) {
+                // Session has been previously submitted to apexd, but didn't complete all the
+                // pre-reboot verification, perhaps because the device rebooted in the meantime.
+                // Greedily re-trigger the pre-reboot verification.
+                Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
+                        + "verified, resuming pre-reboot verification");
+                mBgHandler.post(() -> preRebootVerification(session));
+                return;
+            }
+            if (!apexSessionInfo.isActivated) {
+                // In all the remaining cases apexd will try to apply the session again at next
+                // boot. Nothing to do here for now.
+                Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
+                        + "at boot didn't activate nor fail. This usually means that apexd will "
+                        + "retry at next reboot.");
+                return;
+            }
+        }
+        // The APEX part of the session is activated, proceed with the installation of APKs.
+        if (!installApksInSession(session)) {
+            session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
+                    "APK installation for staged session " + session.sessionId + " failed.");
             return;
         }
-        if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
-            session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
-                    "APEX activation failed. Check logcat messages from apexd for "
-                                  + "more information.");
+        session.setStagedSessionApplied();
+    }
+
+    private String findFirstAPKInDir(File stageDir) {
+        if (stageDir != null && stageDir.exists()) {
+            for (File file : stageDir.listFiles()) {
+                if (file.getAbsolutePath().toLowerCase().endsWith(".apk")) {
+                    return file.getAbsolutePath();
+                }
+            }
         }
-        if (apexSessionInfo.isVerified) {
-            // Session has been previously submitted to apexd, but didn't complete all the
-            // pre-reboot verification, perhaps because the device rebooted in the meantime.
-            // Greedily re-trigger the pre-reboot verification.
-            mBgHandler.post(() -> preRebootVerification(session));
+        return null;
+    }
+
+    private PackageInstallerSession createAndWriteApkSession(
+            @NonNull PackageInstallerSession originalSession) {
+        // TODO(b/123629153): support split APKs.
+        if (originalSession.stageDir == null) {
+            Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
+            return null;
         }
-        if (apexSessionInfo.isActivated) {
-            session.setStagedSessionApplied();
-            // TODO(b/118865310) if multi-package proceed with the installation of APKs.
+        String apkFilePath = findFirstAPKInDir(originalSession.stageDir);
+        if (apkFilePath == null) {
+            Slog.w(TAG, "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath());
+            return null;
         }
-        // In every other case apexd will retry to apply the session at next boot.
+        File apkFile = new File(apkFilePath);
+
+        PackageInstaller.SessionParams params = originalSession.params.copy();
+        params.isStaged = false;
+        int apkSessionId = mPi.createSession(
+                params, originalSession.getInstallerPackageName(), originalSession.userId);
+        PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
+
+        try {
+            apkSession.open();
+            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
+                    ParcelFileDescriptor.MODE_READ_ONLY);
+            long sizeBytes = pfd.getStatSize();
+            if (sizeBytes < 0) {
+                Slog.e(TAG, "Unable to get size of: " + apkFilePath);
+                return null;
+            }
+            apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
+            return null;
+        }
+        return apkSession;
+    }
+
+    private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
+                                     int originalSessionId) {
+        final LocalIntentReceiver receiver = new LocalIntentReceiver();
+        apkSession.commit(receiver.getIntentSender(), false);
+        final Intent result = receiver.getResult();
+        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == PackageInstaller.STATUS_SUCCESS) {
+            return true;
+        }
+        Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " ["
+                + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+        return false;
+    }
+
+    private boolean installApksInSession(@NonNull PackageInstallerSession session) {
+        if (!session.isMultiPackage() && !isApexSession(session)) {
+            // APK single-packaged staged session. Do a regular install.
+            PackageInstallerSession apkSession = createAndWriteApkSession(session);
+            if (apkSession == null) {
+                return false;
+            }
+            return commitApkSession(apkSession, session.sessionId);
+        } else if (session.isMultiPackage()) {
+            // For multi-package staged sessions containing APKs, we identify which child sessions
+            // contain an APK, and with those then create a new multi-package group of sessions,
+            // carrying over all the session parameters and unmarking them as staged. On commit the
+            // sessions will be installed atomically.
+            List<PackageInstallerSession> childSessions;
+            synchronized (mStagedSessions) {
+                childSessions =
+                        Arrays.stream(session.getChildSessionIds())
+                                // Retrieve cached sessions matching ids.
+                                .mapToObj(i -> mStagedSessions.get(i))
+                                // Filter only the ones containing APKs.s
+                                .filter(childSession -> !isApexSession(childSession))
+                                .collect(Collectors.toList());
+            }
+            if (childSessions.isEmpty()) {
+                // APEX-only multi-package staged session, nothing to do.
+                return true;
+            }
+            PackageInstaller.SessionParams params = session.params.copy();
+            params.isStaged = false;
+            int apkParentSessionId = mPi.createSession(
+                    params, session.getInstallerPackageName(), session.userId);
+            PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+            try {
+                apkParentSession.open();
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to prepare multi-package session for staged session "
+                        + session.sessionId);
+                return false;
+            }
+
+            for (PackageInstallerSession sessionToClone : childSessions) {
+                PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone);
+                if (apkChildSession == null) {
+                    return false;
+                }
+                apkParentSession.addChildSessionId(apkChildSession.sessionId);
+            }
+            return commitApkSession(apkParentSession, session.sessionId);
+        }
+        // APEX single-package staged session, nothing to do.
+        return true;
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
@@ -336,9 +500,36 @@
         } else {
             // Session had already being marked ready. Start the checks to verify if there is any
             // follow-up work.
-            // TODO(b/118865310): should this be synchronous to ensure it completes before
-            //                    systemReady() finishes?
-            mBgHandler.post(() -> resumeSession(session));
+            resumeSession(session);
+        }
+    }
+
+    private static class LocalIntentReceiver {
+        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                             IIntentReceiver finishedReceiver, String requiredPermission,
+                             Bundle options) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4f20590..d0f192d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1109,14 +1109,7 @@
 
     @Override
     public int getManagedProfileBadge(@UserIdInt int userId) {
-        int callingUserId = UserHandle.getCallingUserId();
-        if (callingUserId != userId && !hasManageUsersPermission()) {
-            if (!isSameProfileGroupNoChecks(callingUserId, userId)) {
-                throw new SecurityException(
-                        "You need MANAGE_USERS permission to: check if specified user a " +
-                        "managed profile outside your profile group");
-            }
-        }
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getManagedProfileBadge");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null ? userInfo.profileBadge : 0;
@@ -1125,14 +1118,7 @@
 
     @Override
     public boolean isManagedProfile(int userId) {
-        int callingUserId = UserHandle.getCallingUserId();
-        if (callingUserId != userId && !hasManageUsersPermission()) {
-            if (!isSameProfileGroupNoChecks(callingUserId, userId)) {
-                throw new SecurityException(
-                        "You need MANAGE_USERS permission to: check if specified user a " +
-                        "managed profile outside your profile group");
-            }
-        }
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isManagedProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isManagedProfile();
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 7d2dd65..23705db 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -480,7 +480,7 @@
             final String apkPath = pkg.baseCodePath;
             final ApplicationInfo appInfo = pkg.applicationInfo;
             final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
-            if (appInfo.isPrivilegedApp() || appInfo.isCodeIntegrityPreferred()) {
+            if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()) {
                 // Privileged apps prefer to load trusted code so they don't use compiled views.
                 // If the app is not privileged but prefers code integrity, also avoid compiling
                 // views.
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index abbddf3..2213901 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -740,10 +740,10 @@
     }
 
     /**
-     * Generates log if the archive located at {@code fileName} has uncompressed dex file and so
-     * files that can be direclty mapped.
+     * Generates log if the archive located at {@code fileName} has uncompressed dex file that can
+     * be direclty mapped.
      */
-    public static boolean auditUncompressedCodeInApk(String fileName) {
+    public static boolean auditUncompressedDexInApk(String fileName) {
         StrictJarFile jarFile = null;
         try {
             jarFile = new StrictJarFile(fileName,
@@ -762,16 +762,6 @@
                         Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
                                 entry.getName());
                     }
-                } else if (entry.getName().endsWith(".so")) {
-                    if (entry.getMethod() != ZipEntry.STORED) {
-                        allCorrect = false;
-                        Slog.w(TAG, "APK " + fileName + " has compressed native code " +
-                                entry.getName());
-                    } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
-                        allCorrect = false;
-                        Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
-                                entry.getName());
-                    }
                 }
             }
             return allCorrect;
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 848cee0..173d9a0 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -254,6 +254,9 @@
     public boolean isIncidentReportApprover() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
     }
+    public boolean isAppPredictor() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 22780e6..a4413f9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1724,6 +1724,12 @@
                 // this app is the incident report approver, then it gets the permission.
                 allowed = true;
             }
+            if (!allowed && bp.isAppPredictor()
+                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                        PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
+                // Special permissions for the system app predictor.
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index a2c8dac..4186154 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -24,11 +24,13 @@
 import android.os.SystemClock;
 import android.service.attention.AttentionService;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Class responsible for checking if the user is currently paying attention to the phone and
@@ -79,6 +81,11 @@
      */
     private int mWakefulness;
 
+    /**
+     * Describes how many times in a row was the timeout extended.
+     */
+    private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
+
     @VisibleForTesting
     final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() {
 
@@ -95,6 +102,8 @@
                     }
                     if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
                         mOnUserAttention.run();
+                    } else {
+                        resetConsecutiveExtensionCount();
                     }
                 }
             }
@@ -176,6 +185,7 @@
     public int onUserActivity(long eventTime, int event) {
         switch (event) {
             case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
+                mConsecutiveTimeoutExtendedCount.incrementAndGet();
                 return 0;
             case PowerManager.USER_ACTIVITY_EVENT_OTHER:
             case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
@@ -183,6 +193,7 @@
             case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
                 cancelCurrentRequestIfAny();
                 mLastUserActivityTime = eventTime;
+                resetConsecutiveExtensionCount();
                 return 1;
             default:
                 if (DEBUG) {
@@ -196,6 +207,7 @@
         mWakefulness = wakefulness;
         if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
             cancelCurrentRequestIfAny();
+            resetConsecutiveExtensionCount();
         }
     }
 
@@ -206,6 +218,13 @@
         }
     }
 
+    private void resetConsecutiveExtensionCount() {
+        final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
+        if (previousCount > 0) {
+            StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
+        }
+    }
+
     @VisibleForTesting
     int getRequestCode() {
         return (int) (mLastUserActivityTime % Integer.MAX_VALUE);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3be6480..3ccd234 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -22,6 +22,7 @@
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.SynchronousUserSwitchObserver;
@@ -42,6 +43,7 @@
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
+import android.os.BatterySaverPolicyConfig;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2875,8 +2877,7 @@
     @VisibleForTesting
     void updatePowerRequestFromBatterySaverPolicy(DisplayPowerRequest displayPowerRequest) {
         PowerSaveState state = mBatterySaverPolicy.
-                getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS,
-                        mBatterySaverController.isEnabled());
+                getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS);
         displayPowerRequest.lowPowerMode = state.batterySaverEnabled;
         displayPowerRequest.screenLowPowerBrightnessFactor = state.brightnessFactor;
     }
@@ -4451,8 +4452,7 @@
         public PowerSaveState getPowerSaveState(@ServiceType int serviceType) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverPolicy.getBatterySaverPolicy(
-                        serviceType, mBatterySaverController.isEnabled());
+                return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4497,6 +4497,36 @@
         }
 
         @Override // Binder call
+        public boolean setAdaptivePowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, "setAdaptivePowerSavePolicy");
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        public boolean setAdaptivePowerSaveEnabled(boolean enabled) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, "setAdaptivePowerSaveEnabled");
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         public int getPowerSaveMode() {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER, null);
             final long ident = Binder.clearCallingIdentity();
@@ -4846,8 +4876,7 @@
 
         @Override
         public PowerSaveState getLowPowerState(@ServiceType int serviceType) {
-            return mBatterySaverPolicy.getBatterySaverPolicy(serviceType,
-                    mBatterySaverController.isEnabled());
+            return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/power/PowerManagerShellCommand.java b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
index 46115d8..18b8f0e 100644
--- a/services/core/java/com/android/server/power/PowerManagerShellCommand.java
+++ b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
@@ -41,6 +41,8 @@
         final PrintWriter pw = getOutPrintWriter();
         try {
             switch(cmd) {
+                case "set-adaptive-power-saver-enabled":
+                    return runSetAdaptiveEnabled();
                 case "set-mode":
                     return runSetMode();
                 default:
@@ -52,6 +54,11 @@
         return -1;
     }
 
+    private int runSetAdaptiveEnabled() throws RemoteException {
+        mInterface.setAdaptivePowerSaveEnabled(Boolean.parseBoolean(getNextArgRequired()));
+        return 0;
+    }
+
     private int runSetMode() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         int mode = -1;
@@ -72,6 +79,8 @@
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.println("");
+        pw.println("  set-adaptive-power-saver-enabled [true|false]");
+        pw.println("    enables or disables adaptive power saver.");
         pw.println("  set-mode MODE");
         pw.println("    sets the power mode of the device to MODE.");
         pw.println("    1 turns low power mode on and 0 turns low power mode off.");
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 ab2807a..94bb3ea 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -23,6 +23,7 @@
 import android.content.IntentFilter;
 import android.hardware.power.V1_0.PowerHint;
 import android.os.BatteryManager;
+import android.os.BatterySaverPolicyConfig;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -42,6 +43,7 @@
 import com.android.server.LocalServices;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener;
+import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
 import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
@@ -74,16 +76,25 @@
     private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>();
 
     @GuardedBy("mLock")
-    private boolean mEnabled;
+    private boolean mFullEnabled;
+
+    @GuardedBy("mLock")
+    private boolean mAdaptiveEnabled;
 
     @GuardedBy("mLock")
     private boolean mIsPluggedIn;
 
     /**
-     * Previously enabled or not; only for the event logging. Only use it from
+     * Whether full was previously enabled or not; only for the event logging. Only use it from
      * {@link #handleBatterySaverStateChanged}.
      */
-    private boolean mPreviouslyEnabled;
+    private boolean mFullPreviouslyEnabled;
+
+    /**
+     * Whether adaptive was previously enabled or not; only for the event logging. Only use it from
+     * {@link #handleBatterySaverStateChanged}.
+     */
+    private boolean mAdaptivePreviouslyEnabled;
 
     @GuardedBy("mLock")
     private boolean mIsInteractive;
@@ -104,7 +115,9 @@
     public static final int REASON_SETTING_CHANGED = 8;
     public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9;
     public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10;
-    public static final int REASON_STICKY_RESTORE_OFF = 13;
+    public static final int REASON_STICKY_RESTORE_OFF = 11;
+    public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 12;
+    public static final int REASON_TIMEOUT = 13;
 
     /**
      * Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
@@ -124,7 +137,7 @@
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_ON:
                 case Intent.ACTION_SCREEN_OFF:
-                    if (!isEnabled()) {
+                    if (!isPolicyEnabled()) {
                         updateBatterySavingStats();
                         return; // No need to send it if not enabled.
                     }
@@ -199,7 +212,7 @@
 
     @Override
     public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
-        if (!isEnabled()) {
+        if (!isPolicyEnabled()) {
             return; // No need to send it if not enabled.
         }
         mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED);
@@ -248,22 +261,98 @@
     @VisibleForTesting
     public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
-            if (mEnabled == enable) {
+            if (mFullEnabled == enable) {
                 return;
             }
-            mEnabled = enable;
+            mFullEnabled = enable;
 
-            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
+            if (updatePolicyLevelLocked()) {
+                mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
+            }
         }
     }
 
-    /** @return whether battery saver is enabled or not. */
+    private boolean updatePolicyLevelLocked() {
+        if (mFullEnabled) {
+            return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL);
+        } else if (mAdaptiveEnabled) {
+            return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE);
+        } else {
+            return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF);
+        }
+    }
+
+    /**
+     * @return whether battery saver is enabled or not. This takes into
+     * account whether a policy says to advertise isEnabled so this can be propagated externally.
+     */
     public boolean isEnabled() {
         synchronized (mLock) {
-            return mEnabled;
+            return mFullEnabled
+                    || (mAdaptiveEnabled && mBatterySaverPolicy.shouldAdvertiseIsEnabled());
         }
     }
 
+    /**
+     * @return whether battery saver policy is enabled or not. This does not take into account
+     * whether a policy says to advertise isEnabled, so this shouldn't be propagated externally.
+     */
+    private boolean isPolicyEnabled() {
+        synchronized (mLock) {
+            return mFullEnabled || mAdaptiveEnabled;
+        }
+    }
+
+    boolean isFullEnabled() {
+        synchronized (mLock) {
+            return mFullEnabled;
+        }
+    }
+
+    boolean isAdaptiveEnabled() {
+        synchronized (mLock) {
+            return mAdaptiveEnabled;
+        }
+    }
+
+    boolean setAdaptivePolicyLocked(String settings, String deviceSpecificSettings, int reason) {
+        return setAdaptivePolicyLocked(
+                BatterySaverPolicy.Policy.fromSettings(settings, deviceSpecificSettings),
+                reason);
+    }
+
+    boolean setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason) {
+        return setAdaptivePolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason);
+    }
+
+    boolean setAdaptivePolicyLocked(Policy policy, int reason) {
+        if (mBatterySaverPolicy.setAdaptivePolicyLocked(policy)) {
+            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
+            return true;
+        }
+        return false;
+    }
+
+    boolean resetAdaptivePolicyLocked(int reason) {
+        if (mBatterySaverPolicy.resetAdaptivePolicyLocked()) {
+            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
+            return true;
+        }
+        return false;
+    }
+
+    boolean setAdaptivePolicyEnabledLocked(boolean enabled, int reason) {
+        if (mAdaptiveEnabled == enabled) {
+            return false;
+        }
+        mAdaptiveEnabled = enabled;
+        if (updatePolicyLevelLocked()) {
+            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
+            return true;
+        }
+        return false;
+    }
+
     /** @return whether device is in interactive state. */
     public boolean isInteractive() {
         synchronized (mLock) {
@@ -280,7 +369,7 @@
      * @return true if launch boost should currently be disabled.
      */
     public boolean isLaunchBoostDisabled() {
-        return isEnabled() && mBatterySaverPolicy.isLaunchBoostDisabled();
+        return isPolicyEnabled() && mBatterySaverPolicy.isLaunchBoostDisabled();
     }
 
     /**
@@ -293,6 +382,9 @@
      * - When battery saver becomes deactivated.
      * - When battery saver is on and the interactive state changes.
      * - When battery saver is on and the battery saver policy changes.
+     * - When adaptive battery saver becomes activated.
+     * - When adaptive battery saver becomes deactivated.
+     * - When adaptive battery saver policy changes.
      */
     void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
         final LowPowerModeListener[] listeners;
@@ -302,17 +394,22 @@
         final ArrayMap<String, String> fileValues;
 
         synchronized (mLock) {
+            enabled = mFullEnabled || mAdaptiveEnabled;
+
             EventLogTags.writeBatterySaverMode(
-                    mPreviouslyEnabled ? 1 : 0, // Previously off or on.
-                    mEnabled ? 1 : 0, // Now off or on.
+                    mFullPreviouslyEnabled ? 1 : 0, // Previously off or on.
+                    mAdaptivePreviouslyEnabled ? 1 : 0, // Previously off or on.
+                    mFullEnabled ? 1 : 0, // Now off or on.
+                    mAdaptiveEnabled ? 1 : 0, // Now off or on.
                     isInteractive ?  1 : 0, // Device interactive state.
-                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "",
+                    enabled ? mBatterySaverPolicy.toEventLogString() : "",
                     reason);
-            mPreviouslyEnabled = mEnabled;
+
+            mFullPreviouslyEnabled = mFullEnabled;
+            mAdaptivePreviouslyEnabled = mAdaptiveEnabled;
 
             listeners = mListeners.toArray(new LowPowerModeListener[0]);
 
-            enabled = mEnabled;
             mIsInteractive = isInteractive;
 
             if (enabled) {
@@ -324,7 +421,7 @@
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
         if (pmi != null) {
-            pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
+            pmi.powerHint(PowerHint.LOW_POWER, isEnabled() ? 1 : 0);
         }
 
         updateBatterySavingStats();
@@ -342,13 +439,13 @@
         if (sendBroadcast) {
 
             if (DEBUG) {
-                Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
+                Slog.i(TAG, "Sending broadcasts for mode: " + isEnabled());
             }
 
             // Send the broadcasts and notify the listeners. We only do this when the battery saver
             // mode changes, but not when only the screen state changes.
             Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
-                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
+                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, isEnabled())
                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
 
@@ -364,8 +461,7 @@
 
             for (LowPowerModeListener listener : listeners) {
                 final PowerSaveState result =
-                        mBatterySaverPolicy.getBatterySaverPolicy(
-                                listener.getServiceType(), enabled);
+                        mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType());
                 listener.onLowPowerModeChanged(result);
             }
         }
@@ -389,7 +485,8 @@
                 return;
             }
             mBatterySavingStats.transitionState(
-                    mEnabled ? BatterySaverState.ON : BatterySaverState.OFF,
+                    mFullEnabled ? BatterySaverState.ON :
+                            (mAdaptiveEnabled ? BatterySaverState.ADAPTIVE : BatterySaverState.OFF),
                     isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE,
                     dozeMode);
         }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
index bd8baeb..a77d133 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
@@ -53,8 +53,8 @@
     private void updateLocationState(BatterySaverController caller) {
         final boolean kill =
                 (caller.getBatterySaverPolicy().getGpsMode()
-                        == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&
-                caller.isEnabled() && !caller.isInteractive();
+                        == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF)
+                        && !caller.isInteractive();
 
         if (DEBUG) {
             Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location.");
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 48a041e..1d74e1f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -15,16 +15,17 @@
  */
 package com.android.server.power.batterysaver;
 
+import android.annotation.IntDef;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.BatterySaverPolicyConfig;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerSaveState;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.KeyValueListParser;
@@ -39,8 +40,12 @@
 import com.android.server.power.PowerManagerService;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Class to decide whether to turn on battery saver mode for specific services.
@@ -48,12 +53,12 @@
  * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy.
  * Do not call out with the lock held, such as AccessibilityManager. (Settings provider is okay.)
  *
- * Test: atest com.android.server.power.batterysaver.BatterySaverPolicyTest.java
+ * Test: atest com.android.server.power.batterysaver.BatterySaverPolicyTest
  */
 public class BatterySaverPolicy extends ContentObserver {
     private static final String TAG = "BatterySaverPolicy";
 
-    public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
+    static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
 
     private static final String KEY_GPS_MODE = "gps_mode";
     private static final String KEY_VIBRATION_DISABLED = "vibration_disabled";
@@ -81,6 +86,14 @@
      * If set to true, Data Saver WILL NOT be turned on when Battery Saver is turned on.
      */
     private static final String KEY_ACTIVATE_DATASAVER_DISABLED = "datasaver_disabled";
+
+    /**
+     * {@code true} if the Policy should advertise to the rest of the system that battery saver
+     * is enabled. This advertising could cause other system components to change their
+     * behavior. This will not affect other policy flags and what they change.
+     */
+    private static final String KEY_ADVERTISE_IS_ENABLED = "advertise_is_enabled";
+
     private static final String KEY_LAUNCH_BOOST_DISABLED = "launch_boost_disabled";
     private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
     private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
@@ -91,13 +104,38 @@
     private static final String KEY_AOD_DISABLED = "aod_disabled";
     // Go into deep Doze as soon as the screen turns off.
     private static final String KEY_QUICK_DOZE_ENABLED = "quick_doze_enabled";
-    private static final String KEY_SEND_TRON_LOG = "send_tron_log";
 
     private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
     private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
 
-    private static final Policy sDefaultPolicy = new Policy(
+    @VisibleForTesting
+    static final Policy OFF_POLICY = new Policy(
+            1f,    /* adjustBrightnessFactor */
+            false, /* advertiseIsEnabled */
+            false, /* deferFullBackup */
+            false, /* deferKeyValueBackup */
+            false, /* disableAnimation */
+            false, /* disableAod */
+            false, /* disableLaunchBoost */
+            false, /* disableOptionalSensors */
+            false, /* disableSoundTrigger */
+            false, /* disableVibration */
+            false, /* enableAdjustBrightness */
+            false, /* enableDataSaver */
+            false, /* enableFireWall */
+            false, /* enableQuickDoze */
+            new ArrayMap<>(), /* filesForInteractive */
+            new ArrayMap<>(), /* filesForNoninteractive */
+            false, /* forceAllAppsStandby */
+            false, /* forceBackgroundCheck */
+            PowerManager.LOCATION_MODE_NO_CHANGE /* gpsMode */
+    );
+
+    private static final Policy DEFAULT_ADAPTIVE_POLICY = OFF_POLICY;
+
+    private static final Policy DEFAULT_FULL_POLICY = new Policy(
             0.5f,  /* adjustBrightnessFactor */
+            true,  /* advertiseIsEnabled */
             true,  /* deferFullBackup */
             true,  /* deferKeyValueBackup */
             false, /* disableAnimation */
@@ -114,8 +152,7 @@
             new ArrayMap<>(), /* filesForNoninteractive */
             true, /* forceAllAppsStandby */
             true, /* forceBackgroundCheck */
-            PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, /* gpsMode */
-            false /* sendTronLog */
+            PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF /* gpsMode */
     );
 
     private final Object mLock;
@@ -130,6 +167,12 @@
     @GuardedBy("mLock")
     private String mDeviceSpecificSettingsSource; // For dump() only.
 
+    @GuardedBy("mLock")
+    private String mAdaptiveSettings;
+
+    @GuardedBy("mLock")
+    private String mAdaptiveDeviceSpecificSettings;
+
     /**
      * A short string describing which battery saver is now enabled, which we dump in the eventlog.
      */
@@ -149,8 +192,32 @@
     @GuardedBy("mLock")
     private boolean mAccessibilityEnabled;
 
+    /** The current default adaptive policy. */
     @GuardedBy("mLock")
-    private Policy mCurrPolicy = sDefaultPolicy;
+    private Policy mDefaultAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
+
+    /** The policy that will be used for adaptive battery saver. */
+    @GuardedBy("mLock")
+    private Policy mAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
+
+    /** The policy to be used for full battery saver. */
+    @GuardedBy("mLock")
+    private Policy mFullPolicy = DEFAULT_FULL_POLICY;
+
+    @IntDef(prefix = {"POLICY_LEVEL_"}, value = {
+            POLICY_LEVEL_OFF,
+            POLICY_LEVEL_ADAPTIVE,
+            POLICY_LEVEL_FULL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface PolicyLevel {}
+
+    static final int POLICY_LEVEL_OFF = 0;
+    static final int POLICY_LEVEL_ADAPTIVE = 1;
+    static final int POLICY_LEVEL_FULL = 2;
+
+    @GuardedBy("mLock")
+    private int mPolicyLevel = POLICY_LEVEL_OFF;
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -181,7 +248,11 @@
         mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
         mContentResolver.registerContentObserver(Settings.Global.getUriFor(
-                Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
+                Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
+        mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS), false, this);
+        mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS), false, this);
 
         final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
 
@@ -241,8 +312,16 @@
                 mDeviceSpecificSettingsSource = "(overlay)";
             }
 
-            // Update.
-            updateConstantsLocked(setting, deviceSpecificSetting);
+            final String adaptiveSetting =
+                    getGlobalSetting(Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS);
+            final String adaptiveDeviceSpecificSetting = getGlobalSetting(
+                    Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS);
+
+            if (!updateConstantsLocked(setting, deviceSpecificSetting,
+                    adaptiveSetting, adaptiveDeviceSpecificSetting)) {
+                // Nothing of note changed.
+                return;
+            }
 
             listeners = mListeners.toArray(new BatterySaverPolicyListener[0]);
         }
@@ -258,123 +337,91 @@
     @GuardedBy("mLock")
     @VisibleForTesting
     void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
+        updateConstantsLocked(setting, deviceSpecificSetting, "", "");
+    }
+
+    /** @return true if the currently active policy changed. */
+    private boolean updateConstantsLocked(String setting, String deviceSpecificSetting,
+            String adaptiveSetting, String adaptiveDeviceSpecificSetting) {
+        setting = TextUtils.emptyIfNull(setting);
+        deviceSpecificSetting = TextUtils.emptyIfNull(deviceSpecificSetting);
+        adaptiveSetting = TextUtils.emptyIfNull(adaptiveSetting);
+        adaptiveDeviceSpecificSetting = TextUtils.emptyIfNull(adaptiveDeviceSpecificSetting);
+
+        if (setting.equals(mSettings)
+                && deviceSpecificSetting.equals(mDeviceSpecificSettings)
+                && adaptiveSetting.equals(mAdaptiveSettings)
+                && adaptiveDeviceSpecificSetting.equals(mAdaptiveDeviceSpecificSettings)) {
+            return false;
+        }
+
         mSettings = setting;
         mDeviceSpecificSettings = deviceSpecificSetting;
+        mAdaptiveSettings = adaptiveSetting;
+        mAdaptiveDeviceSpecificSettings = adaptiveDeviceSpecificSetting;
 
         if (DEBUG) {
             Slog.i(TAG, "mSettings=" + mSettings);
             Slog.i(TAG, "mDeviceSpecificSettings=" + mDeviceSpecificSettings);
+            Slog.i(TAG, "mAdaptiveSettings=" + mAdaptiveSettings);
+            Slog.i(TAG, "mAdaptiveDeviceSpecificSettings=" + mAdaptiveDeviceSpecificSettings);
         }
 
-        final KeyValueListParser parser = new KeyValueListParser(',');
-
-        // Device-specific parameters.
-        try {
-            parser.setString(deviceSpecificSetting);
-        } catch (IllegalArgumentException e) {
-            Slog.wtf(TAG, "Bad device specific battery saver constants: "
-                    + deviceSpecificSetting);
+        boolean changed = false;
+        Policy newFullPolicy = Policy.fromSettings(setting, deviceSpecificSetting,
+                DEFAULT_FULL_POLICY);
+        if (mPolicyLevel == POLICY_LEVEL_FULL && !mFullPolicy.equals(newFullPolicy)) {
+            changed = true;
         }
+        mFullPolicy = newFullPolicy;
 
-        final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
-        final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");
-
-        // Non-device-specific parameters.
-        try {
-            parser.setString(setting);
-        } catch (IllegalArgumentException e) {
-            Slog.wtf(TAG, "Bad battery saver constants: " + setting);
+        mDefaultAdaptivePolicy = Policy.fromSettings(adaptiveSetting, adaptiveDeviceSpecificSetting,
+                DEFAULT_ADAPTIVE_POLICY);
+        if (mPolicyLevel == POLICY_LEVEL_ADAPTIVE
+                && !mAdaptivePolicy.equals(mDefaultAdaptivePolicy)) {
+            changed = true;
         }
+        // This will override any config set by an external source. This should be fine for now.
+        // TODO: make sure it doesn't override what's set externally
+        mAdaptivePolicy = mDefaultAdaptivePolicy;
 
-        float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
-                sDefaultPolicy.adjustBrightnessFactor);
-        boolean deferFullBackup = parser.getBoolean(KEY_FULLBACKUP_DEFERRED,
-                sDefaultPolicy.deferFullBackup);
-        boolean deferKeyValueBackup = parser.getBoolean(KEY_KEYVALUE_DEFERRED,
-                sDefaultPolicy.deferKeyValueBackup);
-        boolean disableAnimation = parser.getBoolean(KEY_ANIMATION_DISABLED,
-                sDefaultPolicy.disableAnimation);
-        boolean disableAod = parser.getBoolean(KEY_AOD_DISABLED, sDefaultPolicy.disableAod);
-        boolean disableLaunchBoost = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED,
-                sDefaultPolicy.disableLaunchBoost);
-        boolean disableOptionalSensors = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED,
-                sDefaultPolicy.disableOptionalSensors);
-        boolean disableSoundTrigger = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED,
-                sDefaultPolicy.disableSoundTrigger);
-        boolean disableVibrationConfig = parser.getBoolean(KEY_VIBRATION_DISABLED,
-                sDefaultPolicy.disableVibration);
-        boolean enableAdjustBrightness = !parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED,
-                !sDefaultPolicy.enableAdjustBrightness);
-        boolean enableDataSaver = !parser.getBoolean(KEY_ACTIVATE_DATASAVER_DISABLED,
-                !sDefaultPolicy.enableDataSaver);
-        boolean enableFirewall = !parser.getBoolean(KEY_ACTIVATE_FIREWALL_DISABLED,
-                !sDefaultPolicy.enableFirewall);
-        boolean enableQuickDoze = parser.getBoolean(KEY_QUICK_DOZE_ENABLED,
-                sDefaultPolicy.enableQuickDoze);
-        boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
-                sDefaultPolicy.forceAllAppsStandby);
-        boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
-                sDefaultPolicy.forceBackgroundCheck);
-        int gpsMode = parser.getInt(KEY_GPS_MODE, sDefaultPolicy.gpsMode);
-        boolean sendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, sDefaultPolicy.sendTronLog);
+        updatePolicyDependenciesLocked();
 
-        mCurrPolicy = new Policy(
-                adjustBrightnessFactor,
-                deferFullBackup,
-                deferKeyValueBackup,
-                disableAnimation,
-                disableAod,
-                disableLaunchBoost,
-                disableOptionalSensors,
-                disableSoundTrigger,
-                /* disableVibration */
-                disableVibrationConfig,
-                enableAdjustBrightness,
-                enableDataSaver,
-                enableFirewall,
-                enableQuickDoze,
-                /* filesForInteractive */
-                (new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(),
-                /* filesForNoninteractive */
-                (new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
-                forceAllAppsStandby,
-                forceBackgroundCheck,
-                gpsMode,
-                sendTronLog
-        );
+        return changed;
+    }
 
-        // Update the effective policy.
-        mDisableVibrationEffective = mCurrPolicy.disableVibration
+    @GuardedBy("mLock")
+    private void updatePolicyDependenciesLocked() {
+        final Policy currPolicy = getCurrentPolicyLocked();
+        // Update the effective vibration policy.
+        mDisableVibrationEffective = currPolicy.disableVibration
                 && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on.
 
         final StringBuilder sb = new StringBuilder();
 
-        if (mCurrPolicy.forceAllAppsStandby) sb.append("A");
-        if (mCurrPolicy.forceBackgroundCheck) sb.append("B");
+        if (currPolicy.forceAllAppsStandby) sb.append("A");
+        if (currPolicy.forceBackgroundCheck) sb.append("B");
 
         if (mDisableVibrationEffective) sb.append("v");
-        if (mCurrPolicy.disableAnimation) sb.append("a");
-        if (mCurrPolicy.disableSoundTrigger) sb.append("s");
-        if (mCurrPolicy.deferFullBackup) sb.append("F");
-        if (mCurrPolicy.deferKeyValueBackup) sb.append("K");
-        if (mCurrPolicy.enableFirewall) sb.append("f");
-        if (mCurrPolicy.enableDataSaver) sb.append("d");
-        if (mCurrPolicy.enableAdjustBrightness) sb.append("b");
+        if (currPolicy.disableAnimation) sb.append("a");
+        if (currPolicy.disableSoundTrigger) sb.append("s");
+        if (currPolicy.deferFullBackup) sb.append("F");
+        if (currPolicy.deferKeyValueBackup) sb.append("K");
+        if (currPolicy.enableFirewall) sb.append("f");
+        if (currPolicy.enableDataSaver) sb.append("d");
+        if (currPolicy.enableAdjustBrightness) sb.append("b");
 
-        if (mCurrPolicy.disableLaunchBoost) sb.append("l");
-        if (mCurrPolicy.disableOptionalSensors) sb.append("S");
-        if (mCurrPolicy.disableAod) sb.append("o");
-        if (mCurrPolicy.enableQuickDoze) sb.append("q");
-        if (mCurrPolicy.sendTronLog) sb.append("t");
+        if (currPolicy.disableLaunchBoost) sb.append("l");
+        if (currPolicy.disableOptionalSensors) sb.append("S");
+        if (currPolicy.disableAod) sb.append("o");
+        if (currPolicy.enableQuickDoze) sb.append("q");
 
-        sb.append(mCurrPolicy.gpsMode);
+        sb.append(currPolicy.gpsMode);
 
         mEventLogKeys = sb.toString();
-
-        mBatterySavingStats.setSendTronLog(mCurrPolicy.sendTronLog);
     }
 
-    private static class Policy {
+    static class Policy {
         /**
          * This is the flag to decide the how much to adjust the screen brightness. This is
          * the float value from 0 to 1 where 1 means don't change brightness.
@@ -385,6 +432,16 @@
         public final float adjustBrightnessFactor;
 
         /**
+         * {@code true} if the Policy should advertise to the rest of the system that battery saver
+         * is enabled. This advertising could cause other system components to change their
+         * behavior. This will not affect other policy flags and what they change.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ADVERTISE_IS_ENABLED
+         */
+        public final boolean advertiseIsEnabled;
+
+        /**
          * {@code true} if full backup is deferred in battery saver mode.
          *
          * @see Settings.Global#BATTERY_SAVER_CONSTANTS
@@ -504,13 +561,11 @@
          */
         public final int gpsMode;
 
-        /**
-         * Whether BatterySavingStats should send tron events.
-         */
-        public final boolean sendTronLog;
+        private final int mHashCode;
 
         Policy(
                 float adjustBrightnessFactor,
+                boolean advertiseIsEnabled,
                 boolean deferFullBackup,
                 boolean deferKeyValueBackup,
                 boolean disableAnimation,
@@ -527,10 +582,10 @@
                 ArrayMap<String, String> filesForNoninteractive,
                 boolean forceAllAppsStandby,
                 boolean forceBackgroundCheck,
-                int gpsMode,
-                boolean sendTronLog) {
+                int gpsMode) {
 
             this.adjustBrightnessFactor = adjustBrightnessFactor;
+            this.advertiseIsEnabled = advertiseIsEnabled;
             this.deferFullBackup = deferFullBackup;
             this.deferKeyValueBackup = deferKeyValueBackup;
             this.disableAnimation = disableAnimation;
@@ -548,95 +603,333 @@
             this.forceAllAppsStandby = forceAllAppsStandby;
             this.forceBackgroundCheck = forceBackgroundCheck;
             this.gpsMode = gpsMode;
-            this.sendTronLog = sendTronLog;
+
+            mHashCode = Objects.hash(
+                    adjustBrightnessFactor,
+                    advertiseIsEnabled,
+                    deferFullBackup,
+                    deferKeyValueBackup,
+                    disableAnimation,
+                    disableAod,
+                    disableLaunchBoost,
+                    disableOptionalSensors,
+                    disableSoundTrigger,
+                    disableVibration,
+                    enableAdjustBrightness,
+                    enableDataSaver,
+                    enableFirewall,
+                    enableQuickDoze,
+                    filesForInteractive,
+                    filesForNoninteractive,
+                    forceAllAppsStandby,
+                    forceBackgroundCheck,
+                    gpsMode);
+        }
+
+        static Policy fromConfig(BatterySaverPolicyConfig config) {
+            if (config == null) {
+                Slog.e(TAG, "Null config passed down to BatterySaverPolicy");
+                return OFF_POLICY;
+            }
+
+            // Device-specific parameters.
+            Map<String, String> deviceSpecificSettings = config.getDeviceSpecificSettings();
+            final String cpuFreqInteractive =
+                    deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_INTERACTIVE, "");
+            final String cpuFreqNoninteractive =
+                    deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_NONINTERACTIVE, "");
+
+            return new Policy(
+                    config.getAdjustBrightnessFactor(),
+                    config.getAdvertiseIsEnabled(),
+                    config.getDeferFullBackup(),
+                    config.getDeferKeyValueBackup(),
+                    config.getDisableAnimation(),
+                    config.getDisableAod(),
+                    config.getDisableLaunchBoost(),
+                    config.getDisableOptionalSensors(),
+                    config.getDisableSoundTrigger(),
+                    config.getDisableVibration(),
+                    config.getEnableAdjustBrightness(),
+                    config.getEnableDataSaver(),
+                    config.getEnableFirewall(),
+                    config.getEnableQuickDoze(),
+                    /* filesForInteractive */
+                    (new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(),
+                    /* filesForNoninteractive */
+                    (new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
+                    config.getForceAllAppsStandby(),
+                    config.getForceBackgroundCheck(),
+                    config.getGpsMode()
+            );
+        }
+
+        static Policy fromSettings(String settings, String deviceSpecificSettings) {
+            return fromSettings(settings, deviceSpecificSettings, OFF_POLICY);
+        }
+
+        static Policy fromSettings(String settings, String deviceSpecificSettings,
+                Policy defaultPolicy) {
+            final KeyValueListParser parser = new KeyValueListParser(',');
+
+            // Device-specific parameters.
+            try {
+                parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
+            } catch (IllegalArgumentException e) {
+                Slog.wtf(TAG, "Bad device specific battery saver constants: "
+                        + deviceSpecificSettings);
+            }
+
+            final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
+            final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");
+
+            // Non-device-specific parameters.
+            try {
+                parser.setString(settings == null ? "" : settings);
+            } catch (IllegalArgumentException e) {
+                Slog.wtf(TAG, "Bad battery saver constants: " + settings);
+            }
+
+            float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
+                    defaultPolicy.adjustBrightnessFactor);
+            boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
+                    defaultPolicy.advertiseIsEnabled);
+            boolean deferFullBackup = parser.getBoolean(KEY_FULLBACKUP_DEFERRED,
+                    defaultPolicy.deferFullBackup);
+            boolean deferKeyValueBackup = parser.getBoolean(KEY_KEYVALUE_DEFERRED,
+                    defaultPolicy.deferKeyValueBackup);
+            boolean disableAnimation = parser.getBoolean(KEY_ANIMATION_DISABLED,
+                    defaultPolicy.disableAnimation);
+            boolean disableAod = parser.getBoolean(KEY_AOD_DISABLED, defaultPolicy.disableAod);
+            boolean disableLaunchBoost = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED,
+                    defaultPolicy.disableLaunchBoost);
+            boolean disableOptionalSensors = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED,
+                    defaultPolicy.disableOptionalSensors);
+            boolean disableSoundTrigger = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED,
+                    defaultPolicy.disableSoundTrigger);
+            boolean disableVibrationConfig = parser.getBoolean(KEY_VIBRATION_DISABLED,
+                    defaultPolicy.disableVibration);
+            boolean enableAdjustBrightness = !parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED,
+                    !defaultPolicy.enableAdjustBrightness);
+            boolean enableDataSaver = !parser.getBoolean(KEY_ACTIVATE_DATASAVER_DISABLED,
+                    !defaultPolicy.enableDataSaver);
+            boolean enableFirewall = !parser.getBoolean(KEY_ACTIVATE_FIREWALL_DISABLED,
+                    !defaultPolicy.enableFirewall);
+            boolean enableQuickDoze = parser.getBoolean(KEY_QUICK_DOZE_ENABLED,
+                    defaultPolicy.enableQuickDoze);
+            boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
+                    defaultPolicy.forceAllAppsStandby);
+            boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
+                    defaultPolicy.forceBackgroundCheck);
+            int gpsMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.gpsMode);
+
+            return new Policy(
+                    adjustBrightnessFactor,
+                    advertiseIsEnabled,
+                    deferFullBackup,
+                    deferKeyValueBackup,
+                    disableAnimation,
+                    disableAod,
+                    disableLaunchBoost,
+                    disableOptionalSensors,
+                    disableSoundTrigger,
+                    /* disableVibration */
+                    disableVibrationConfig,
+                    enableAdjustBrightness,
+                    enableDataSaver,
+                    enableFirewall,
+                    enableQuickDoze,
+                    /* filesForInteractive */
+                    (new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(),
+                    /* filesForNoninteractive */
+                    (new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
+                    forceAllAppsStandby,
+                    forceBackgroundCheck,
+                    gpsMode
+            );
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof Policy)) return false;
+            Policy other = (Policy) obj;
+            return Float.compare(other.adjustBrightnessFactor, adjustBrightnessFactor) == 0
+                    && advertiseIsEnabled == other.advertiseIsEnabled
+                    && deferFullBackup == other.deferFullBackup
+                    && deferKeyValueBackup == other.deferKeyValueBackup
+                    && disableAnimation == other.disableAnimation
+                    && disableAod == other.disableAod
+                    && disableLaunchBoost == other.disableLaunchBoost
+                    && disableOptionalSensors == other.disableOptionalSensors
+                    && disableSoundTrigger == other.disableSoundTrigger
+                    && disableVibration == other.disableVibration
+                    && enableAdjustBrightness == other.enableAdjustBrightness
+                    && enableDataSaver == other.enableDataSaver
+                    && enableFirewall == other.enableFirewall
+                    && enableQuickDoze == other.enableQuickDoze
+                    && forceAllAppsStandby == other.forceAllAppsStandby
+                    && forceBackgroundCheck == other.forceBackgroundCheck
+                    && gpsMode == other.gpsMode
+                    && filesForInteractive.equals(other.filesForInteractive)
+                    && filesForNoninteractive.equals(other.filesForNoninteractive);
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
         }
     }
 
     /**
-     * Get the {@link PowerSaveState} based on {@paramref type} and {@paramref realMode}.
+     * Get the {@link PowerSaveState} based on the current policy level.
      * The result will have {@link PowerSaveState#batterySaverEnabled} and some other
      * parameters when necessary.
      *
-     * @param type     type of the service, one of {@link ServiceType}
-     * @param realMode whether the battery saver is on by default
+     * @param type   type of the service, one of {@link ServiceType}
      * @return State data that contains battery saver data
      */
-    public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
+    public PowerSaveState getBatterySaverPolicy(@ServiceType int type) {
         synchronized (mLock) {
+            final Policy currPolicy = getCurrentPolicyLocked();
             final PowerSaveState.Builder builder = new PowerSaveState.Builder()
-                    .setGlobalBatterySaverEnabled(realMode);
-            if (!realMode) {
-                return builder.setBatterySaverEnabled(realMode)
-                        .build();
-            }
+                    .setGlobalBatterySaverEnabled(currPolicy.advertiseIsEnabled);
             switch (type) {
                 case ServiceType.GPS:
-                    return builder.setBatterySaverEnabled(realMode)
-                            .setGpsMode(mCurrPolicy.gpsMode)
+                    boolean isEnabled = currPolicy.advertiseIsEnabled
+                            || currPolicy.gpsMode != PowerManager.LOCATION_MODE_NO_CHANGE;
+                    return builder.setBatterySaverEnabled(isEnabled)
+                            .setGpsMode(currPolicy.gpsMode)
                             .build();
                 case ServiceType.ANIMATION:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.disableAnimation)
+                    return builder.setBatterySaverEnabled(currPolicy.disableAnimation)
                             .build();
                 case ServiceType.FULL_BACKUP:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.deferFullBackup)
+                    return builder.setBatterySaverEnabled(currPolicy.deferFullBackup)
                             .build();
                 case ServiceType.KEYVALUE_BACKUP:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.deferKeyValueBackup)
+                    return builder.setBatterySaverEnabled(currPolicy.deferKeyValueBackup)
                             .build();
                 case ServiceType.NETWORK_FIREWALL:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.enableFirewall)
+                    return builder.setBatterySaverEnabled(currPolicy.enableFirewall)
                             .build();
                 case ServiceType.SCREEN_BRIGHTNESS:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.enableAdjustBrightness)
-                            .setBrightnessFactor(mCurrPolicy.adjustBrightnessFactor)
+                    return builder.setBatterySaverEnabled(currPolicy.enableAdjustBrightness)
+                            .setBrightnessFactor(currPolicy.adjustBrightnessFactor)
                             .build();
                 case ServiceType.DATA_SAVER:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.enableDataSaver)
+                    return builder.setBatterySaverEnabled(currPolicy.enableDataSaver)
                             .build();
                 case ServiceType.SOUND:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.disableSoundTrigger)
+                    return builder.setBatterySaverEnabled(currPolicy.disableSoundTrigger)
                             .build();
                 case ServiceType.VIBRATION:
                     return builder.setBatterySaverEnabled(mDisableVibrationEffective)
                             .build();
                 case ServiceType.FORCE_ALL_APPS_STANDBY:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.forceAllAppsStandby)
+                    return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby)
                             .build();
                 case ServiceType.FORCE_BACKGROUND_CHECK:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.forceBackgroundCheck)
+                    return builder.setBatterySaverEnabled(currPolicy.forceBackgroundCheck)
                             .build();
                 case ServiceType.OPTIONAL_SENSORS:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.disableOptionalSensors)
+                    return builder.setBatterySaverEnabled(currPolicy.disableOptionalSensors)
                             .build();
                 case ServiceType.AOD:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.disableAod)
+                    return builder.setBatterySaverEnabled(currPolicy.disableAod)
                             .build();
                 case ServiceType.QUICK_DOZE:
-                    return builder.setBatterySaverEnabled(mCurrPolicy.enableQuickDoze)
+                    return builder.setBatterySaverEnabled(currPolicy.enableQuickDoze)
                             .build();
                 default:
-                    return builder.setBatterySaverEnabled(realMode)
+                    return builder.setBatterySaverEnabled(currPolicy.advertiseIsEnabled)
                             .build();
             }
         }
     }
 
+    /**
+     * Sets the current policy.
+     *
+     * @return true if the policy level was changed.
+     */
+    boolean setPolicyLevel(@PolicyLevel int level) {
+        synchronized (mLock) {
+            if (mPolicyLevel == level) {
+                return false;
+            }
+            switch (level) {
+                case POLICY_LEVEL_FULL:
+                case POLICY_LEVEL_ADAPTIVE:
+                case POLICY_LEVEL_OFF:
+                    mPolicyLevel = level;
+                    break;
+                default:
+                    Slog.wtf(TAG, "setPolicyLevel invalid level given: " + level);
+                    return false;
+            }
+            updatePolicyDependenciesLocked();
+            return true;
+        }
+    }
+
+    /** @return true if the current policy changed and the policy level is ADAPTIVE. */
+    boolean setAdaptivePolicyLocked(Policy p) {
+        if (p == null) {
+            Slog.wtf(TAG, "setAdaptivePolicy given null policy");
+            return false;
+        }
+        if (mAdaptivePolicy.equals(p)) {
+            return false;
+        }
+
+        mAdaptivePolicy = p;
+        if (mPolicyLevel == POLICY_LEVEL_ADAPTIVE) {
+            updatePolicyDependenciesLocked();
+            return true;
+        }
+        return false;
+    }
+
+    /** @return true if the current policy changed and the policy level is ADAPTIVE. */
+    boolean resetAdaptivePolicyLocked() {
+        return setAdaptivePolicyLocked(mDefaultAdaptivePolicy);
+    }
+
+    private Policy getCurrentPolicyLocked() {
+        switch (mPolicyLevel) {
+            case POLICY_LEVEL_FULL:
+                return mFullPolicy;
+            case POLICY_LEVEL_ADAPTIVE:
+                return mAdaptivePolicy;
+            case POLICY_LEVEL_OFF:
+            default:
+                return OFF_POLICY;
+        }
+    }
+
     public int getGpsMode() {
         synchronized (mLock) {
-            return mCurrPolicy.gpsMode;
+            return getCurrentPolicyLocked().gpsMode;
         }
     }
 
     public ArrayMap<String, String> getFileValues(boolean interactive) {
         synchronized (mLock) {
-            return interactive ? mCurrPolicy.filesForInteractive
-                    : mCurrPolicy.filesForNoninteractive;
+            return interactive ? getCurrentPolicyLocked().filesForInteractive
+                    : getCurrentPolicyLocked().filesForNoninteractive;
         }
     }
 
     public boolean isLaunchBoostDisabled() {
         synchronized (mLock) {
-            return mCurrPolicy.disableLaunchBoost;
+            return getCurrentPolicyLocked().disableLaunchBoost;
+        }
+    }
+
+    boolean shouldAdvertiseIsEnabled() {
+        synchronized (mLock) {
+            return getCurrentPolicyLocked().advertiseIsEnabled;
         }
     }
 
@@ -658,40 +951,72 @@
             pw.println("  Settings: " + mDeviceSpecificSettingsSource);
             pw.println("    value: " + mDeviceSpecificSettings);
 
-            pw.println();
+            pw.println("  Adaptive Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS);
+            pw.println("    value: " + mAdaptiveSettings);
+            pw.println("  Adaptive Device Specific Settings: "
+                    + Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS);
+            pw.println("    value: " + mAdaptiveDeviceSpecificSettings);
+
             pw.println("  mAccessibilityEnabled=" + mAccessibilityEnabled);
-            pw.println("  " + KEY_VIBRATION_DISABLED + ":config=" + mCurrPolicy.disableVibration);
-            pw.println("  " + KEY_VIBRATION_DISABLED + ":effective=" + mDisableVibrationEffective);
-            pw.println("  " + KEY_ANIMATION_DISABLED + "=" + mCurrPolicy.disableAnimation);
-            pw.println("  " + KEY_FULLBACKUP_DEFERRED + "=" + mCurrPolicy.deferFullBackup);
-            pw.println("  " + KEY_KEYVALUE_DEFERRED + "=" + mCurrPolicy.deferKeyValueBackup);
-            pw.println("  " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !mCurrPolicy.enableFirewall);
-            pw.println("  " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !mCurrPolicy.enableDataSaver);
-            pw.println("  " + KEY_LAUNCH_BOOST_DISABLED + "=" + mCurrPolicy.disableLaunchBoost);
-            pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "="
-                    + !mCurrPolicy.enableAdjustBrightness);
-            pw.println(
-                    "  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mCurrPolicy.adjustBrightnessFactor);
-            pw.println("  " + KEY_GPS_MODE + "=" + mCurrPolicy.gpsMode);
-            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mCurrPolicy.forceAllAppsStandby);
-            pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + mCurrPolicy.forceBackgroundCheck);
-            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "="
-                    + mCurrPolicy.disableOptionalSensors);
-            pw.println("  " + KEY_AOD_DISABLED + "=" + mCurrPolicy.disableAod);
-            pw.println("  " + KEY_SOUNDTRIGGER_DISABLED + "=" + mCurrPolicy.disableSoundTrigger);
-            pw.println("  " + KEY_QUICK_DOZE_ENABLED + "=" + mCurrPolicy.enableQuickDoze);
-            pw.println("  " + KEY_SEND_TRON_LOG + "=" + mCurrPolicy.sendTronLog);
-            pw.println();
+            pw.println("  mPolicyLevel=" + mPolicyLevel);
 
-            pw.print("  Interactive File values:\n");
-            dumpMap(pw, "    ", mCurrPolicy.filesForInteractive);
-            pw.println();
-
-            pw.print("  Noninteractive File values:\n");
-            dumpMap(pw, "    ", mCurrPolicy.filesForNoninteractive);
+            dumpPolicyLocked(pw, "  ", "full", mFullPolicy);
+            dumpPolicyLocked(pw, "  ", "default adaptive", mDefaultAdaptivePolicy);
+            dumpPolicyLocked(pw, "  ", "current adaptive", mAdaptivePolicy);
         }
     }
 
+    private void dumpPolicyLocked(PrintWriter pw, String indent, String label, Policy p) {
+        pw.println();
+        pw.print(indent);
+        pw.println("Policy '" + label + "'");
+        pw.print(indent);
+        pw.println("  " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
+        pw.print(indent);
+        pw.println("  " + KEY_VIBRATION_DISABLED + ":config=" + p.disableVibration);
+        // mDisableVibrationEffective is based on the currently selected policy
+        pw.print(indent);
+        pw.println("  " + KEY_VIBRATION_DISABLED + ":effective=" + (p.disableVibration
+                && !mAccessibilityEnabled));
+        pw.print(indent);
+        pw.println("  " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation);
+        pw.print(indent);
+        pw.println("  " + KEY_FULLBACKUP_DEFERRED + "=" + p.deferFullBackup);
+        pw.print(indent);
+        pw.println("  " + KEY_KEYVALUE_DEFERRED + "=" + p.deferKeyValueBackup);
+        pw.print(indent);
+        pw.println("  " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !p.enableFirewall);
+        pw.print(indent);
+        pw.println("  " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !p.enableDataSaver);
+        pw.print(indent);
+        pw.println("  " + KEY_LAUNCH_BOOST_DISABLED + "=" + p.disableLaunchBoost);
+        pw.println(
+                "    " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + !p.enableAdjustBrightness);
+        pw.print(indent);
+        pw.println("  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor);
+        pw.print(indent);
+        pw.println("  " + KEY_GPS_MODE + "=" + p.gpsMode);
+        pw.print(indent);
+        pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby);
+        pw.print(indent);
+        pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck);
+        pw.println(
+                "    " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + p.disableOptionalSensors);
+        pw.print(indent);
+        pw.println("  " + KEY_AOD_DISABLED + "=" + p.disableAod);
+        pw.print(indent);
+        pw.println("  " + KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger);
+        pw.print(indent);
+        pw.println("  " + KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze);
+
+        pw.print("    Interactive File values:\n");
+        dumpMap(pw, "      ", p.filesForInteractive);
+        pw.println();
+
+        pw.print("    Noninteractive File values:\n");
+        dumpMap(pw, "      ", p.filesForNoninteractive);
+    }
+
     private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) {
         if (map == null) {
             return;
@@ -710,6 +1035,7 @@
     public void setAccessibilityEnabledForTest(boolean enabled) {
         synchronized (mLock) {
             mAccessibilityEnabled = enabled;
+            updatePolicyDependenciesLocked();
         }
     }
 }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 404e450..e6fa500 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -24,8 +24,10 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.os.BatterySaverPolicyConfig;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
@@ -46,8 +48,7 @@
  * IMPORTANT: This class shares the power manager lock, which is very low in the lock hierarchy.
  * Do not call out with the lock held. (Settings provider is okay.)
  *
- * Test:
-  atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+ * Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest
  */
 public class BatterySaverStateMachine {
     private static final String TAG = "BatterySaverStateMachine";
@@ -57,6 +58,8 @@
 
     private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
 
+    private static final long ADAPTIVE_CHANGE_TIMEOUT_MS = 24 * 60 * 60 * 1000L;
+
     private final Context mContext;
     private final BatterySaverController mBatterySaverController;
 
@@ -157,6 +160,13 @@
     @GuardedBy("mLock")
     private String mLastChangedStrReason;
 
+    /**
+     * The last time adaptive battery saver was changed by an external service, using elapsed
+     * realtime as the timebase.
+     */
+    @GuardedBy("mLock")
+    private long mLastAdaptiveBatterySaverChangedExternallyElapsed;
+
     private final ContentObserver mSettingsObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean selfChange) {
@@ -178,10 +188,6 @@
                 com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
     }
 
-    private boolean isBatterySaverEnabled() {
-        return mBatterySaverController.isEnabled();
-    }
-
     private boolean isAutoBatterySaverConfiguredLocked() {
         return mSettingBatterySaverTriggerThreshold > 0;
     }
@@ -392,6 +398,36 @@
         }
     }
 
+    /**
+     * Enable or disable the current adaptive battery saver policy. This may not change what's in
+     * effect if full battery saver is also enabled.
+     */
+    public boolean setAdaptiveBatterySaverEnabled(boolean enabled) {
+        if (DEBUG) {
+            Slog.d(TAG, "setAdaptiveBatterySaverEnabled: enabled=" + enabled);
+        }
+        synchronized (mLock) {
+            mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime();
+            return mBatterySaverController.setAdaptivePolicyEnabledLocked(
+                    enabled, BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED);
+        }
+    }
+
+    /**
+     * Change the adaptive battery saver policy.
+     */
+    public boolean setAdaptiveBatterySaverPolicy(BatterySaverPolicyConfig config) {
+        if (DEBUG) {
+            Slog.d(TAG, "setAdaptiveBatterySaverPolicy: config=" + config);
+        }
+
+        synchronized (mLock) {
+            mLastAdaptiveBatterySaverChangedExternallyElapsed = SystemClock.elapsedRealtime();
+            return mBatterySaverController.setAdaptivePolicyLocked(config,
+                    BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isBatteryLowLocked() {
         final boolean percentageLow =
@@ -427,12 +463,26 @@
         if (!isBatteryLowLocked()) {
             updateSnoozingLocked(false, "Battery not low");
         }
+
+        if (SystemClock.elapsedRealtime() - mLastAdaptiveBatterySaverChangedExternallyElapsed
+                > ADAPTIVE_CHANGE_TIMEOUT_MS) {
+            mBatterySaverController.setAdaptivePolicyEnabledLocked(
+                    false, BatterySaverController.REASON_TIMEOUT);
+            mBatterySaverController.resetAdaptivePolicyLocked(
+                    BatterySaverController.REASON_TIMEOUT);
+        }
+
         if (mIsPowered) {
             updateSnoozingLocked(false, "Plugged in");
             enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
                     BatterySaverController.REASON_PLUGGED_IN,
                     "Plugged in");
 
+            if (mBatteryLevel >= 80 /* Arbitrary level */) {
+                mBatterySaverController.setAdaptivePolicyEnabledLocked(
+                        false, BatterySaverController.REASON_PLUGGED_IN);
+            }
+
         } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) {
             if (mSettingBatterySaverStickyAutoDisableEnabled
                     && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold) {
@@ -472,7 +522,7 @@
         // do nothing if automatic battery saver mode = PERCENTAGE and low warning threshold = 0%
     }
 
-  /**
+    /**
      * {@link com.android.server.power.PowerManagerService} calls it when
      * {@link android.os.PowerManager#setPowerSaveMode} is called.
      *
@@ -501,7 +551,7 @@
             Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
                     + " reason=" + strReason + "(" + intReason + ")");
         }
-        final boolean wasEnabled = mBatterySaverController.isEnabled();
+        final boolean wasEnabled = mBatterySaverController.isFullEnabled();
 
         if (wasEnabled == enable) {
             if (DEBUG) {
@@ -523,9 +573,10 @@
                 // When battery saver is disabled manually (while battery saver is enabled)
                 // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
                 // We resume auto-BS once the battery level is not low, or the device is plugged in.
-                if (isBatterySaverEnabled() && isBatteryLowLocked()) {
+                if (mBatterySaverController.isFullEnabled() && isBatteryLowLocked()) {
                     updateSnoozingLocked(true, "Manual snooze");
                 }
+                // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true
             }
         }
 
@@ -622,6 +673,17 @@
 
             pw.print("  Enabled=");
             pw.println(mBatterySaverController.isEnabled());
+            pw.print("    full=");
+            pw.println(mBatterySaverController.isFullEnabled());
+            pw.print("    adaptive=");
+            pw.print(mBatterySaverController.isAdaptiveEnabled());
+            if (mBatterySaverController.isAdaptiveEnabled()) {
+                pw.print(" (advertise=");
+                pw.print(
+                        mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
+                pw.print(")");
+            }
+            pw.println();
 
             pw.print("  mLastChangedIntReason=");
             pw.println(mLastChangedIntReason);
@@ -657,6 +719,9 @@
             pw.println(mSettingBatterySaverTriggerThreshold);
             pw.print("  mBatterySaverStickyBehaviourDisabled=");
             pw.println(mBatterySaverStickyBehaviourDisabled);
+
+            pw.print("  mLastAdaptiveBatterySaverChangedExternallyElapsed=");
+            pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed);
         }
     }
 
@@ -666,6 +731,12 @@
 
             proto.write(BatterySaverStateMachineProto.ENABLED,
                     mBatterySaverController.isEnabled());
+            proto.write(BatterySaverStateMachineProto.IS_FULL_ENABLED,
+                    mBatterySaverController.isFullEnabled());
+            proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED,
+                    mBatterySaverController.isAdaptiveEnabled());
+            proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED,
+                    mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
 
             proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted);
             proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded);
@@ -692,6 +763,11 @@
                             .SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_THRESHOLD,
                     mSettingBatterySaverStickyAutoDisableThreshold);
 
+            proto.write(
+                    BatterySaverStateMachineProto
+                            .LAST_ADAPTIVE_BATTERY_SAVER_CHANGED_EXTERNALLY_ELAPSED,
+                    mLastAdaptiveBatterySaverChangedExternallyElapsed);
+
             proto.end(token);
         }
     }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 79b44eb..3dbc007 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.power.batterysaver;
 
-import android.metrics.LogMaker;
 import android.os.BatteryManagerInternal;
 import android.os.SystemClock;
 import android.util.ArrayMap;
@@ -24,9 +23,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 
@@ -57,9 +53,10 @@
     interface BatterySaverState {
         int OFF = 0;
         int ON = 1;
+        int ADAPTIVE = 2;
 
         int SHIFT = 0;
-        int BITS = 1;
+        int BITS = 2;
         int MASK = (1 << BITS) - 1;
 
         static int fromIndex(int index) {
@@ -141,7 +138,6 @@
     }
 
     private BatteryManagerInternal mBatteryManagerInternal;
-    private final MetricsLogger mMetricsLogger;
 
     private static final int STATE_NOT_INITIALIZED = -1;
     private static final int STATE_CHARGING = -2;
@@ -171,28 +167,11 @@
     @GuardedBy("mLock")
     private long mLastBatterySaverDisabledTime = 0;
 
-    private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper();
-
-    @VisibleForTesting
-    @GuardedBy("mLock")
-    private boolean mSendTronLog;
-
     /** Visible for unit tests */
     @VisibleForTesting
-    public BatterySavingStats(Object lock, MetricsLogger metricsLogger) {
+    public BatterySavingStats(Object lock) {
         mLock = lock;
         mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
-        mMetricsLogger = metricsLogger;
-    }
-
-    public BatterySavingStats(Object lock) {
-        this(lock, new MetricsLogger());
-    }
-
-    public void setSendTronLog(boolean send) {
-        synchronized (mLock) {
-            mSendTronLog = send;
-        }
     }
 
     private BatteryManagerInternal getBatteryManagerInternal() {
@@ -324,7 +303,6 @@
 
         endLastStateLocked(now, batteryLevel, batteryPercent);
         startNewStateLocked(newState, now, batteryLevel, batteryPercent);
-        mMetricsLoggerHelper.transitionStateLocked(newState, now, batteryLevel, batteryPercent);
     }
 
     @GuardedBy("mLock")
@@ -471,61 +449,4 @@
                 onStat.totalBatteryDrainPercent,
                 onStat.drainPerHour() / 1000.0));
     }
-
-    @VisibleForTesting
-    class MetricsLoggerHelper {
-        private int mLastState = STATE_NOT_INITIALIZED;
-        private long mStartTime;
-        private int mStartBatteryLevel;
-        private int mStartPercent;
-
-        private static final int STATE_CHANGE_DETECT_MASK =
-                (BatterySaverState.MASK << BatterySaverState.SHIFT) |
-                (InteractiveState.MASK << InteractiveState.SHIFT);
-
-        @GuardedBy("BatterySavingStats.this.mLock")
-        public void transitionStateLocked(
-                int newState, long now, int batteryLevel, int batteryPercent) {
-            final boolean stateChanging =
-                    ((mLastState >= 0) ^ (newState >= 0)) ||
-                    (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
-            if (stateChanging) {
-                if (mLastState >= 0) {
-                    final long deltaTime = now - mStartTime;
-
-                    reportLocked(mLastState, deltaTime, mStartBatteryLevel, mStartPercent,
-                            batteryLevel, batteryPercent);
-                }
-                mStartTime = now;
-                mStartBatteryLevel = batteryLevel;
-                mStartPercent = batteryPercent;
-            }
-            mLastState = newState;
-        }
-
-        @GuardedBy("BatterySavingStats.this.mLock")
-        void reportLocked(int state, long deltaTimeMs,
-                int startBatteryLevelUa, int startBatteryLevelPercent,
-                int endBatteryLevelUa, int endBatteryLevelPercent) {
-            if (!mSendTronLog) {
-                return;
-            }
-            final boolean batterySaverOn =
-                    BatterySaverState.fromIndex(state) != BatterySaverState.OFF;
-            final boolean interactive =
-                    InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE;
-
-            final LogMaker logMaker = new LogMaker(MetricsProto.MetricsEvent.BATTERY_SAVER)
-                    .setSubtype(batterySaverOn ? 1 : 0)
-                    .addTaggedData(MetricsEvent.FIELD_INTERACTIVE, interactive ? 1 : 0)
-                    .addTaggedData(MetricsEvent.FIELD_DURATION_MILLIS, deltaTimeMs)
-                    .addTaggedData(MetricsEvent.FIELD_START_BATTERY_UA, startBatteryLevelUa)
-                    .addTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT,
-                            startBatteryLevelPercent)
-                    .addTaggedData(MetricsEvent.FIELD_END_BATTERY_UA, endBatteryLevelUa)
-                    .addTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT, endBatteryLevelPercent);
-
-            mMetricsLogger.write(logMaker);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 03ec57b..d72270e 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -52,6 +52,7 @@
 import android.service.sms.FinancialSmsService;
 import android.telephony.IFinancialSmsCallback;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.PackageUtils;
 import android.util.Slog;
@@ -145,6 +146,9 @@
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
+        LocalServices.addService(RoleManagerServiceInternal.class,
+                new RoleManagerServiceInternalImpl());
+
         registerUserRemovedReceiver();
     }
 
@@ -382,6 +386,19 @@
         }
     }
 
+    /**
+     * Get all roles and packages hold them.
+     *
+     * @param user The user to query to roles for
+     *
+     * @return The roles and their holders
+     */
+    @NonNull
+    private ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) {
+        RoleUserState userState = getOrCreateUserState(user.getIdentifier());
+        return userState.getRoleHolders();
+    }
+
     private class Stub extends IRoleManager.Stub {
 
         @Override
@@ -676,4 +693,16 @@
             }
         }
     }
+
+    /**
+     * Entry point for internal calls into role manager
+     */
+    private final class RoleManagerServiceInternalImpl extends RoleManagerServiceInternal {
+
+        @NonNull
+        @Override
+        public ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) {
+            return RoleManagerService.this.getRoleHoldersAsUser(user);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerServiceInternal.java b/services/core/java/com/android/server/role/RoleManagerServiceInternal.java
new file mode 100644
index 0000000..3afc3f7
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerServiceInternal.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.role;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+/**
+ * Internal calls into {@link RoleManagerService}
+ */
+public abstract class RoleManagerServiceInternal {
+    /**
+     * Get all roles and packages hold them.
+     *
+     * @param user The user to query to roles for
+     *
+     * @return The roles and their holders
+     */
+    @NonNull
+    public abstract ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(
+            @NonNull UserHandle user);
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 02dcc49..bc68dde 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -376,7 +376,7 @@
 
             version = mVersion;
             packagesHash = mPackagesHash;
-            roles = snapshotRolesLocked();
+            roles = getRoleHolders();
         }
 
         AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
@@ -541,7 +541,7 @@
 
             version = mVersion;
             packagesHash = mPackagesHash;
-            roles = snapshotRolesLocked();
+            roles = getRoleHolders();
         }
 
         long fieldToken = dumpOutputStream.start(fieldName, fieldId);
@@ -570,17 +570,24 @@
         dumpOutputStream.end(fieldToken);
     }
 
-    @GuardedBy("mLock")
-    private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
-        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
-        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
-            String roleName = mRoles.keyAt(i);
-            ArraySet<String> roleHolders = mRoles.valueAt(i);
+    /**
+     * Get the roles and their holders.
+     *
+     * @return A copy of the roles and their holders
+     */
+    @NonNull
+    public ArrayMap<String, ArraySet<String>> getRoleHolders() {
+        synchronized (mLock) {
+            ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
+            for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
+                String roleName = mRoles.keyAt(i);
+                ArraySet<String> roleHolders = mRoles.valueAt(i);
 
-            roleHolders = new ArraySet<>(roleHolders);
-            roles.put(roleName, roleHolders);
+                roleHolders = new ArraySet<>(roleHolders);
+                roles.put(roleName, roleHolders);
+            }
+            return roles;
         }
-        return roles;
     }
 
     /**
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 5eb137b..4c2b3c2 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -47,7 +47,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer;
-import com.android.server.pm.PackageManagerServiceUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -329,15 +328,18 @@
                 int sessionId = packageInstaller.createSession(params);
                 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
 
-                // TODO: Will it always be called "base.apk"? What about splits?
-                // What about apex?
-                File packageDir = new File(data.backupDir, info.getPackageName());
-                File baseApk = new File(packageDir, "base.apk");
-                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+                File packageCode = RollbackStore.getPackageCode(data, info.getPackageName());
+                if (packageCode == null) {
+                    sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+                            "Backup copy of package code inaccessible");
+                    return;
+                }
+
+                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode,
                         ParcelFileDescriptor.MODE_READ_ONLY)) {
                     final long token = Binder.clearCallingIdentity();
                     try {
-                        session.write("base.apk", 0, baseApk.length(), fd);
+                        session.write(packageCode.getName(), 0, packageCode.length(), fd);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
@@ -735,17 +737,19 @@
         VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
 
         // Get information about the currently installed package.
-        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-        PackageParser.Package installedPackage = pm.getPackage(packageName);
-        if (installedPackage == null) {
+        PackageManager pm = mContext.getPackageManager();
+        PackageInfo pkgInfo = null;
+        try {
+            pkgInfo = pm.getPackageInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
             // TODO: Support rolling back fresh package installs rather than
             // fail here. Test this case.
             Log.e(TAG, packageName + " is not installed");
             return false;
         }
-        VersionedPackage installedVersion = new VersionedPackage(packageName,
-                installedPackage.getLongVersionCode());
 
+        VersionedPackage installedVersion = new VersionedPackage(packageName,
+                pkgInfo.getLongVersionCode());
 
         final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName,
                 installedUsers);
@@ -769,21 +773,17 @@
             return false;
         }
 
-        File packageDir = mRollbackStore.packageCodePathForAvailableRollback(data, packageName);
-        packageDir.mkdirs();
-
-        // TODO: Copy by hard link instead to save on cpu and storage space?
-        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, packageDir);
-        if (status != PackageManager.INSTALL_SUCCEEDED) {
-            Log.e(TAG, "Unable to copy package for rollback for " + packageName);
+        try {
+            RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
             return false;
         }
-
         return true;
     }
 
     @Override
-    public void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+    public void restoreUserData(String packageName, int[] userIds, int appId, long ceDataInode,
             String seInfo, int token) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("restoureUserData may only be called by the system.");
@@ -791,23 +791,26 @@
 
         getHandler().post(() -> {
             final RollbackData rollbackData = getRollbackForPackage(packageName);
-            final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName,
-                    rollbackData, userId, appId, ceDataInode, seInfo);
+            for (int userId : userIds) {
+                final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName,
+                        rollbackData, userId, appId, ceDataInode, seInfo);
+
+                // We've updated metadata about this rollback, so save it to flash.
+                if (changedRollbackData) {
+                    try {
+                        mRollbackStore.saveAvailableRollback(rollbackData);
+                    } catch (IOException ioe) {
+                        // TODO(narayan): What is the right thing to do here ? This isn't a fatal
+                        // error, since it will only result in us trying to restore data again,
+                        // which will be a no-op if there's no data available.
+                        Log.e(TAG, "Unable to save available rollback: " + packageName, ioe);
+                    }
+                }
+            }
+
             final PackageManagerInternal pmi = LocalServices.getService(
                     PackageManagerInternal.class);
             pmi.finishPackageInstall(token, false);
-
-            // We've updated metadata about this rollback, so save it to flash.
-            if (changedRollbackData) {
-                try {
-                    mRollbackStore.saveAvailableRollback(rollbackData);
-                } catch (IOException ioe) {
-                    // TODO(narayan): What is the right thing to do here ? This isn't a fatal error,
-                    // since it will only result in us trying to restore data again, which will be
-                    // a no-op if there's no data available.
-                    Log.e(TAG, "Unable to save available rollback: " + packageName, ioe);
-                }
-            }
         });
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index b3cc6de..8e04160 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -18,14 +18,18 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.StatsLog;
 
+import com.android.internal.R;
 import com.android.server.PackageWatchdog;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
@@ -53,10 +57,16 @@
     }
 
     @Override
-    public int onHealthCheckFailed(String packageName, long versionCode) {
+    public int onHealthCheckFailed(VersionedPackage failedPackage) {
+        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
+        if (moduleMetadataPackage == null) {
+            // Ignore failure, no mainline update available
+            return PackageHealthObserverImpact.USER_IMPACT_NONE;
+        }
+
         RollbackInfo rollback =
-                getAvailableRollback(mContext.getSystemService(RollbackManager.class),
-                    packageName, versionCode);
+                getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class),
+                        failedPackage, moduleMetadataPackage);
         if (rollback == null) {
             // Don't handle the notification, no rollbacks available for the package
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
@@ -66,31 +76,46 @@
     }
 
     @Override
-    public boolean execute(String packageName, long versionCode) {
-        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        RollbackInfo rollback = getAvailableRollback(rollbackManager, packageName, versionCode);
-        if (rollback == null) {
-            // Expected a rollback to be available, what happened?
+    public boolean execute(VersionedPackage failedPackage) {
+        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
+        if (moduleMetadataPackage == null) {
+            // Ignore failure, no mainline update available
             return false;
         }
 
-        // TODO(zezeozue): Only rollback if rollback version == failed package version
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager,
+                failedPackage, moduleMetadataPackage);
+        if (rollback == null) {
+            Slog.w(TAG, "Expected rollback but no mainline rollback found for package: [ "
+                    + failedPackage.getPackageName() + "] with versionCode: ["
+                    + failedPackage.getVersionCode() + "]");
+            return false;
+        }
+
+        StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                moduleMetadataPackage.getPackageName(),
+                moduleMetadataPackage.getVersionCode());
         LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
-            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_FAILURE);
-            if (status == PackageInstaller.STATUS_SUCCESS) {
-                // TODO(zezeozue); Log success metrics
-                // Rolledback successfully, no action required by other observers
+            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                    RollbackManager.STATUS_FAILURE);
+            if (status == RollbackManager.STATUS_SUCCESS) {
+                StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                        moduleMetadataPackage.getPackageName(),
+                        moduleMetadataPackage.getVersionCode());
             } else {
-                // TODO(zezeozue); Log failure metrics
-                // Rollback failed other observers should have a shot
+                StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                        moduleMetadataPackage.getPackageName(),
+                        moduleMetadataPackage.getVersionCode());
             }
         });
 
-        // TODO(zezeozue): Log initiated metrics
         mHandler.post(() ->
                 rollbackManager.commitRollback(rollback.getRollbackId(),
-                    Collections.singletonList(new VersionedPackage(packageName, versionCode)),
+                    Collections.singletonList(moduleMetadataPackage),
                     rollbackReceiver.getIntentSender()));
         // Assume rollback executed successfully
         return true;
@@ -109,17 +134,41 @@
         PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
     }
 
-    private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
-            String packageName, long versionCode) {
+    private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager,
+            VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) {
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
+            // We only rollback mainline packages, so check if rollback contains the
+            // module metadata provider, if it does, the rollback is a mainline rollback
+            boolean hasModuleMetadataPackage = false;
+            boolean hasFailedPackage = false;
             for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
-                if (packageName.equals(packageRollback.getPackageName())
+                hasModuleMetadataPackage |= packageRollback.getPackageName().equals(
+                        moduleMetadataPackage.getPackageName());
+                hasFailedPackage |= packageRollback.getPackageName().equals(
+                        failedPackage.getPackageName())
                         && packageRollback.getVersionRolledBackFrom().getVersionCode()
-                        == versionCode) {
-                    return rollback;
-                }
+                        == failedPackage.getVersionCode();
+            }
+            if (hasModuleMetadataPackage && hasFailedPackage) {
+                return rollback;
             }
         }
         return null;
     }
+
+    private VersionedPackage getModuleMetadataPackage() {
+        String packageName = mContext.getResources().getString(
+                R.string.config_defaultModuleMetadataProvider);
+        if (!TextUtils.isEmpty(packageName)) {
+            return null;
+        }
+
+        try {
+            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
+                            packageName, 0 /* flags */).getLongVersionCode());
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Module metadata provider not found");
+            return null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index c70f47d..d8e1f62 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -33,6 +33,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.Files;
 import java.time.Instant;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
@@ -201,11 +202,30 @@
     }
 
     /**
-     * Returns the directory where the code for a package should be stored for
-     * given rollback <code>data</code> and <code>packageName</code>.
+     * Creates a backup copy of the apk or apex for a package.
      */
-    File packageCodePathForAvailableRollback(RollbackData data, String packageName) {
-        return new File(data.backupDir, packageName);
+    static void backupPackageCode(RollbackData data, String packageName, String codePath)
+            throws IOException {
+        File sourceFile = new File(codePath);
+        File targetDir = new File(data.backupDir, packageName);
+        targetDir.mkdirs();
+        File targetFile = new File(targetDir, sourceFile.getName());
+
+        // TODO: Copy by hard link instead to save on cpu and storage space?
+        Files.copy(sourceFile.toPath(), targetFile.toPath());
+    }
+
+    /**
+     * Returns the apk or apex file backed up for the given package.
+     * Returns null if none found.
+     */
+    static File getPackageCode(RollbackData data, String packageName) {
+        File targetDir = new File(data.backupDir, packageName);
+        File[] files = targetDir.listFiles();
+        if (files == null || files.length != 1) {
+            return null;
+        }
+        return files[0];
     }
 
     /**
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c6d2870..2378c57 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -23,6 +23,7 @@
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
 import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -83,6 +84,7 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -96,6 +98,7 @@
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelCpuThreadReader;
+import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
@@ -112,6 +115,7 @@
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.role.RoleManagerServiceInternal;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
@@ -200,7 +204,7 @@
             "zygote64",
     };
 
-    private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8;
+    private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
 
     static final class CompanionHandler extends Handler {
         CompanionHandler(Looper looper) {
@@ -329,7 +333,8 @@
         handlerThread.start();
         mHandler = new CompanionHandler(handlerThread.getLooper());
 
-        mKernelCpuThreadReader = KernelCpuThreadReader.create();
+        mKernelCpuThreadReader =
+                KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
     }
 
     @Override
@@ -1675,8 +1680,8 @@
             return;
         }
         int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
-        if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) {
-            Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES
+        if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) {
+            Slog.w(TAG, "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
                     + " frequencies, but got " + cpuFrequencies.length);
             return;
         }
@@ -1700,9 +1705,17 @@
                 e.writeInt(threadCpuUsage.threadId);
                 e.writeString(processCpuUsage.processName);
                 e.writeString(threadCpuUsage.threadName);
-                for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) {
-                    e.writeInt(cpuFrequencies[k]);
-                    e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+                for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) {
+                    if (k < cpuFrequencies.length) {
+                        e.writeInt(cpuFrequencies[k]);
+                        e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+                    } else {
+                        // If we have no more frequencies to write, we still must write empty data.
+                        // We know that this data is empty (and not just zero) because all
+                        // frequencies are expected to be greater than zero
+                        e.writeInt(0);
+                        e.writeInt(0);
+                    }
                 }
                 pulledData.add(e);
             }
@@ -1781,6 +1794,60 @@
     }
 
     /**
+     * Add a RoleHolder atom for each package that holds a role.
+     *
+     * @param elapsedNanos the time since boot
+     * @param wallClockNanos the time on the clock
+     * @param pulledData the data sink to write to
+     */
+    private void pullRoleHolders(long elapsedNanos, final long wallClockNanos,
+            @NonNull List<StatsLogEventWrapper> pulledData) {
+        long callingToken = Binder.clearCallingIdentity();
+        try {
+            PackageManager pm = mContext.getPackageManager();
+            RoleManagerServiceInternal rm =
+                    LocalServices.getService(RoleManagerServiceInternal.class);
+
+            List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+
+            int numUsers = users.size();
+            for (int userNum = 0; userNum < numUsers; userNum++) {
+                UserHandle user = users.get(userNum).getUserHandle();
+
+                ArrayMap<String, ArraySet<String>> roles = rm.getRoleHoldersAsUser(user);
+
+                int numRoles = roles.size();
+                for (int roleNum = 0; roleNum < numRoles; roleNum++) {
+                    String roleName = roles.keyAt(roleNum);
+                    ArraySet<String> holders = roles.valueAt(roleNum);
+
+                    int numHolders = holders.size();
+                    for (int holderNum = 0; holderNum < numHolders; holderNum++) {
+                        String holderName = holders.valueAt(holderNum);
+
+                        PackageInfo pkg;
+                        try {
+                            pkg = pm.getPackageInfoAsUser(holderName, 0, user.getIdentifier());
+                        } catch (PackageManager.NameNotFoundException e) {
+                            Log.w(TAG, "Role holder " + holderName + " not found");
+                            return;
+                        }
+
+                        StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.ROLE_HOLDER,
+                                elapsedNanos, wallClockNanos);
+                        e.writeInt(pkg.applicationInfo.uid);
+                        e.writeString(holderName);
+                        e.writeString(roleName);
+                        pulledData.add(e);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+    }
+
+    /**
      * Pulls various data.
      */
     @Override // Binder call
@@ -1954,6 +2021,10 @@
                 pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.ROLE_HOLDER: {
+                pullRoleHolders(elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
deleted file mode 100644
index 5f71b0b..0000000
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timezonedetector;
-
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-import android.app.timezonedetector.ITimeZoneDetectorService;
-import android.content.Context;
-import android.util.Slog;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
-    private static final String TAG = "timezonedetector.TimeZoneDetectorService";
-
-    public static class Lifecycle extends SystemService {
-
-        public Lifecycle(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onStart() {
-            TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
-            // Publish the binder service so it can be accessed from other (appropriately
-            // permissioned) processes.
-            publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
-        }
-    }
-
-    private final Context mContext;
-
-    private static TimeZoneDetectorService create(Context context) {
-        return new TimeZoneDetectorService(context);
-    }
-
-    public TimeZoneDetectorService(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public void stubbedCall() {
-        // Empty call for initial tests.
-        Slog.d(TAG, "stubbedCall() called");
-        // TODO: Remove when there are real methods.
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        // TODO: Implement when there is state.
-    }
-}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0ef8a0..071dde7 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2243,12 +2243,9 @@
         synchronized (mLock) {
             mInAmbientMode = inAmbientMode;
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
-            final boolean hasConnection = data != null && data.connection != null;
-            final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
-
             // The wallpaper info is null for image wallpaper, also use the engine in this case.
-            if (hasConnection && (info == null && isAodImageWallpaperEnabled()
-                    || info != null && info.supportsAmbientMode())) {
+            if (data != null && data.connection != null && (data.connection.mInfo == null
+                    || data.connection.mInfo.supportsAmbientMode())) {
                 // TODO(multi-display) Extends this method with specific display.
                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index aa0c62c..38e8785 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2065,7 +2065,8 @@
      * - should be either the topmost in task, or right below the top activity that is finishing
      * If all of these conditions are not met at the same time, the activity cannot be made active.
      */
-    private boolean shouldMakeActive(ActivityRecord activeActivity) {
+    @VisibleForTesting
+    boolean shouldMakeActive(ActivityRecord activeActivity) {
         // If the activity is stopped, stopping, cycle to an active state. We avoid doing
         // this when there is an activity waiting to become translucent as the extra binder
         // calls will lead to noticeable jank. A later call to
@@ -2080,6 +2081,11 @@
             return false;
         }
 
+        if (!mStackSupervisor.readyToResume()) {
+            // Making active is currently deferred (e.g. because an activity launch is in progress).
+            return false;
+        }
+
         if (this.mLaunchTaskBehind) {
             // This activity is being launched from behind, which means that it's not intended to be
             // presented to user right now, even if it's set to be visible.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 95a6f71..1d71d876 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -926,6 +926,10 @@
         if (callerApp != null && callerApp.hasForegroundActivities()) {
             return false;
         }
+        // don't abort if the callerApp is instrumenting with background activity starts privileges
+        if (callerApp != null && callerApp.isInstrumentingWithBackgroundActivityStartPrivileges()) {
+            return false;
+        }
         // don't abort if the callingUid is in the foreground or is a persistent system process
         final int callingUidProcState = mService.getUidStateLocked(callingUid);
         final boolean callingUidHasAnyVisibleWindow =
@@ -976,6 +980,10 @@
         if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
             return false;
         }
+        // don't abort if the callingPackage is a device owner
+        if (mService.getDevicePolicyManager().isDeviceOwnerApp(callingPackage)) {
+            return false;
+        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5fabde4..14c4814 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -151,6 +151,7 @@
 import android.app.WaitResult;
 import android.app.WindowConfiguration;
 import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.usage.UsageStatsManagerInternal;
@@ -349,11 +350,20 @@
 
     /* Global service lock used by the package the owns this service. */
     final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
+    /**
+     * It is the same instance as {@link mGlobalLock}, just declared as a type that the
+     * locked-region-code-injection does't recognize it. It is used to skip wrapping priority
+     * booster for places that are already in the scope of another booster (e.g. computing oom-adj).
+     *
+     * @see WindowManagerThreadPriorityBooster
+     */
+    final Object mGlobalLockWithoutBoost = mGlobalLock;
     ActivityStackSupervisor mStackSupervisor;
     RootActivityContainer mRootActivityContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
     private AppOpsService mAppOpsService;
+    private DevicePolicyManager mDpm;
     /** All active uids in the system. */
     private final SparseArray<Integer> mActiveUids = new SparseArray<>();
     private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
@@ -828,6 +838,13 @@
         return mAppOpsService;
     }
 
+    DevicePolicyManager getDevicePolicyManager() {
+        if (mDpm == null) {
+            mDpm = mContext.getSystemService(DevicePolicyManager.class);
+        }
+        return mDpm;
+    }
+
     boolean hasUserRestriction(String restriction, int userId) {
         return getUserManager().hasUserRestriction(restriction, userId);
     }
@@ -6837,7 +6854,7 @@
 
         @Override
         public WindowProcessController getTopApp() {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 final ActivityRecord top = mRootActivityContainer.getTopResumedActivity();
                 return top != null ? top.app : null;
             }
@@ -6845,7 +6862,7 @@
 
         @Override
         public void rankTaskLayersIfNeeded() {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 if (mRootActivityContainer != null) {
                     mRootActivityContainer.rankTaskLayersIfNeeded();
                 }
@@ -6889,28 +6906,28 @@
 
         @Override
         public void onUidActive(int uid, int procState) {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 mActiveUids.put(uid, procState);
             }
         }
 
         @Override
         public void onUidInactive(int uid) {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 mActiveUids.remove(uid);
             }
         }
 
         @Override
         public void onActiveUidsCleared() {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 mActiveUids.clear();
             }
         }
 
         @Override
         public void onUidProcStateChanged(int uid, int procState) {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 if (mActiveUids.get(uid) != null) {
                     mActiveUids.put(uid, procState);
                 }
@@ -6919,14 +6936,14 @@
 
         @Override
         public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 mPendingTempWhitelist.put(uid, tag);
             }
         }
 
         @Override
         public void onUidRemovedFromPendingTempWhitelist(int uid) {
-            synchronized (mGlobalLock) {
+            synchronized (mGlobalLockWithoutBoost) {
                 mPendingTempWhitelist.remove(uid);
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a52f1af..29ba166 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -978,8 +978,8 @@
     }
 
     @Override
-    void onParentSet() {
-        super.onParentSet();
+    void onParentChanged() {
+        super.onParentChanged();
 
         final Task task = getTask();
 
@@ -2852,10 +2852,13 @@
         // to check super here.
         final boolean reallyAnimating = super.isSelfAnimating();
         final boolean show = !isHidden() || reallyAnimating;
-        if (show && !mLastSurfaceShowing) {
-            mPendingTransaction.show(mSurfaceControl);
-        } else if (!show && mLastSurfaceShowing) {
-            mPendingTransaction.hide(mSurfaceControl);
+
+        if (mSurfaceControl != null) {
+            if (show && !mLastSurfaceShowing) {
+                mPendingTransaction.show(mSurfaceControl);
+            } else if (!show && mLastSurfaceShowing) {
+                mPendingTransaction.hide(mSurfaceControl);
+            }
         }
         if (mThumbnail != null) {
             mThumbnail.setShowing(mPendingTransaction, show);
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index fd72a4a..c90f5bf 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -48,7 +48,7 @@
 
             surface = dc.makeOverlay()
                     .setName("BlackSurface")
-                    .setColorLayer()
+                    .setColorLayer(true)
                     .setParent(null) // TODO: Work-around for b/69259549
                     .build();
             transaction.setWindowCrop(surface, w, h);
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index ce3fb51..d86cf0f 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -532,7 +532,7 @@
     /**
      * Must be called when new parent for the container was set.
      */
-    protected void onParentChanged() {
+    void onParentChanged() {
         final ConfigurationContainer parent = getParent();
         // Removing parent usually means that we've detached this entity to destroy it or to attach
         // to another parent. In both cases we don't need to update the configuration now.
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 8f6b67a..c39060e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -164,7 +164,7 @@
     private SurfaceControl makeDimLayer() {
         return mHost.makeChildSurface(null)
                 .setParent(mHost.getSurfaceControl())
-                .setColorLayer()
+                .setColorLayer(true)
                 .setName("Dim Layer for - " + mHost.getName())
                 .build();
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 080f965..9fb922f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -31,11 +31,14 @@
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -170,6 +173,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
@@ -892,9 +896,7 @@
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
-        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
-                .setOpaque(true)
-                .setContainerLayer();
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true);
         mWindowingLayer = b.setName("Display Root").build();
         mOverlayLayer = b.setName("Display Overlays").build();
 
@@ -4387,8 +4389,8 @@
         }
 
         @Override
-        void onParentSet() {
-            super.onParentSet();
+        void onParentChanged() {
+            super.onParentChanged();
             if (getParent() != null) {
                 mAppAnimationLayer = makeChildSurface(null)
                         .setName("animationLayer")
@@ -4598,7 +4600,7 @@
     @Override
     SurfaceControl.Builder makeChildSurface(WindowContainer child) {
         SurfaceSession s = child != null ? child.getSession() : getSession();
-        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s);
         if (child == null) {
             return b;
         }
@@ -4643,10 +4645,10 @@
     }
 
     @Override
-    void onParentSet() {
+    void onParentChanged() {
         // Since we are the top of the SurfaceControl hierarchy here
         // we create the root surfaces explicitly rather than chaining
-        // up as the default implementation in onParentSet does. So we
+        // up as the default implementation in onParentChanged does. So we
         // explicitly do NOT call super here.
     }
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 2b2231a..3f77e1c 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -164,7 +164,7 @@
 
         if (mInputSurface == null) {
             mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
-                    .getSession()).setContainerLayer()
+                    .getSession()).setContainerLayer(true)
                     .setName("Drag and Drop Input Consumer").build();
         }
         final InputWindowHandle h = getInputWindowHandle();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index ab95e4b..4df5a0b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -22,11 +22,13 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.view.InputApplicationHandle;
 import android.view.InputChannel;
+import android.view.WindowManager;
+
+import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
-import android.view.WindowManager;
+import android.util.Slog;
 
 import java.io.PrintWriter;
 
@@ -87,7 +89,7 @@
         mWindowHandle.scaleFactor = 1.0f;
 
         mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
-                .getSession()).setContainerLayer().setName("Input Consumer " + name)
+                .getSession()).setContainerLayer(true).setName("Input Consumer " + name)
                 .build();
     }
 
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 18fce67..434084c 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -149,7 +149,7 @@
 
         private void createSurface() {
             mSurface = mFactory.get().setName("Letterbox - " + mType)
-                    .setFlags(HIDDEN).setColorLayer().build();
+                    .setFlags(HIDDEN).setColorLayer(true).build();
             mSurface.setLayer(-1);
             mSurface.setColor(new float[]{0, 0, 0});
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 476bd6e..7a86c96 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -252,8 +252,8 @@
     }
 
     @Override
-    void onParentSet() {
-        super.onParentSet();
+    void onParentChanged() {
+        super.onParentChanged();
 
         // Update task bounds if needed.
         adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index cdcb857..9163165 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -83,7 +83,7 @@
         final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
         if (mInputSurface == null) {
             mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
-                    .setContainerLayer()
+                    .setContainerLayer(true)
                     .setName("Drag and Drop Input Consumer").build();
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index e6d646c..3c9a46b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
+import android.hardware.HardwareBuffer;
 import android.os.Process;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -359,7 +360,11 @@
         }
 
         boolean writeBuffer() {
-            final Bitmap bitmap = Bitmap.createHardwareBitmap(mSnapshot.getSnapshot());
+            // TODO(b/116112787) TaskSnapshot needs bookkeep the ColorSpace of the
+            // hardware bitmap when created.
+            final Bitmap bitmap = Bitmap.wrapHardwareBuffer(
+                    HardwareBuffer.createFromGraphicBuffer(mSnapshot.getSnapshot()),
+                    mSnapshot.getColorSpace());
             if (bitmap == null) {
                 Slog.e(TAG, "Invalid task snapshot hw bitmap");
                 return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 938c8b4..2d3e3ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -61,12 +61,12 @@
 import android.util.Slog;
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
-import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 59549e0..6d4bcdc 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -818,7 +818,7 @@
 
         updateSurfaceBounds();
         if (mAnimationBackgroundSurface == null) {
-            mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer()
+            mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
                     .setName("animation background stackId=" + mStackId)
                     .build();
         }
@@ -996,8 +996,8 @@
     }
 
     @Override
-    void onParentSet() {
-        super.onParentSet();
+    void onParentChanged() {
+        super.onParentChanged();
 
         if (getParent() != null || mDisplayContent == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c15afc5..74fb3fa 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -34,8 +34,10 @@
 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
 
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
@@ -729,7 +731,10 @@
             Slog.w(TAG_WM, "Failed to screenshot wallpaper");
             return null;
         }
-        return Bitmap.createHardwareBitmap(wallpaperBuffer);
+        // TODO(b/116112787) Now that hardware bitmap creation can take color space, we
+        // should continue to fix screenshot.
+        return Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(wallpaperBuffer),
+                                         ColorSpace.get(ColorSpace.Named.SRGB));
     }
 
     private WindowState getTopVisibleWallpaper() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1905877..f330569 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -161,23 +161,16 @@
 
     final protected void setParent(WindowContainer<WindowContainer> parent) {
         mParent = parent;
-        // Removing parent usually means that we've detached this entity to destroy it or to attach
-        // to another parent. In both cases we don't need to update the configuration now.
-        if (mParent != null) {
-            // Update full configuration of this container and all its children.
-            onConfigurationChanged(mParent.getConfiguration());
-            // Update merged override configuration of this container and all its children.
-            onMergedOverrideConfigurationChanged();
-        }
-
-        onParentSet();
+        onParentChanged();
     }
 
     /**
      * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
      * Supposed to be overridden and contain actions that should be executed after parent was set.
      */
-    void onParentSet() {
+    @Override
+    void onParentChanged() {
+        super.onParentChanged();
         if (mParent == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 030cc05..37e2200 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -138,6 +138,8 @@
     private volatile boolean mDebugging;
     // Active instrumentation running in process?
     private volatile boolean mInstrumenting;
+    // Active instrumentation with background activity starts privilege running in process?
+    private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges;
     // This process it perceptible by the user.
     private volatile boolean mPerceptible;
     // Set to true when process was launched with a wrapper attached
@@ -362,14 +364,24 @@
         return mAllowBackgroundActivityStarts;
     }
 
-    public void setInstrumenting(boolean instrumenting) {
+    public void setInstrumenting(boolean instrumenting,
+            boolean hasBackgroundActivityStartPrivileges) {
         mInstrumenting = instrumenting;
+        mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges;
     }
 
     boolean isInstrumenting() {
         return mInstrumenting;
     }
 
+    /**
+     * @return true if the instrumentation was started by a holder of
+     * START_ACTIVITIES_FROM_BACKGROUND permission
+     */
+    boolean isInstrumentingWithBackgroundActivityStartPrivileges() {
+        return mInstrumentingWithBackgroundActivityStartPrivileges;
+    }
+
     public void setPerceptible(boolean perceptible) {
         mPerceptible = perceptible;
     }
@@ -394,13 +406,13 @@
     }
 
     public void addPackage(String packageName) {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             mPkgList.add(packageName);
         }
     }
 
     public void clearPackageList() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             mPkgList.clear();
         }
     }
@@ -422,20 +434,18 @@
         }
     }
 
-    public void clearActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            mActivities.clear();
-        }
+    void clearActivities() {
+        mActivities.clear();
     }
 
     public boolean hasActivities() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             return !mActivities.isEmpty();
         }
     }
 
     public boolean hasVisibleActivities() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             for (int i = mActivities.size() - 1; i >= 0; --i) {
                 final ActivityRecord r = mActivities.get(i);
                 if (r.visible) {
@@ -447,7 +457,7 @@
     }
 
     public boolean hasActivitiesOrRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
         }
     }
@@ -462,15 +472,13 @@
         }
     }
 
-    public void finishActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
-            for (int i = 0; i < activities.size(); i++) {
-                final ActivityRecord r = activities.get(i);
-                if (!r.finishing && r.isInStackLocked()) {
-                    r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
-                            null, "finish-heavy", true);
-                }
+    void finishActivities() {
+        ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+        for (int i = 0; i < activities.size(); i++) {
+            final ActivityRecord r = activities.get(i);
+            if (!r.finishing && r.isInStackLocked()) {
+                r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+                        null, "finish-heavy", true);
             }
         }
     }
@@ -531,15 +539,13 @@
     }
 
 
-    public void updateIntentForHeavyWeightActivity(Intent intent) {
-        synchronized (mAtm.mGlobalLock) {
-            if (mActivities.isEmpty()) {
-                return;
-            }
-            ActivityRecord hist = mActivities.get(0);
-            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
-            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
+    void updateIntentForHeavyWeightActivity(Intent intent) {
+        if (mActivities.isEmpty()) {
+            return;
         }
+        ActivityRecord hist = mActivities.get(0);
+        intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
+        intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
     }
 
     boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
@@ -609,7 +615,7 @@
     }
 
     public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             final int activitiesSize = mActivities.size();
             for (int j = 0; j < activitiesSize; j++) {
                 final ActivityRecord r = mActivities.get(j);
@@ -842,18 +848,16 @@
     }
 
     public boolean hasRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             return !mRecentTasks.isEmpty();
         }
     }
 
-    public void clearRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
-            for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
-                mRecentTasks.get(i).clearRootProcess();
-            }
-            mRecentTasks.clear();
+    void clearRecentTasks() {
+        for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+            mRecentTasks.get(i).clearRootProcess();
         }
+        mRecentTasks.clear();
     }
 
     public void appEarlyNotResponding(String annotation, Runnable killAppCallback) {
@@ -907,19 +911,19 @@
     }
 
     public void onTopProcChanged() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             mAtm.mVrController.onTopProcChangedLocked(this);
         }
     }
 
     public boolean isHomeProcess() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             return this == mAtm.mHomeProcess;
         }
     }
 
     public boolean isPreviousProcess() {
-        synchronized (mAtm.mGlobalLock) {
+        synchronized (mAtm.mGlobalLockWithoutBoost) {
             return this == mAtm.mPreviousProcess;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 62e7200..05c19b5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -747,8 +747,8 @@
     }
 
     @Override
-    void onParentSet() {
-        super.onParentSet();
+    void onParentChanged() {
+        super.onParentChanged();
         setDrawnStateEvaluated(false /*evaluated*/);
 
         getDisplayContent().reapplyMagnificationSpec();
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 0d888dc..988d75c 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -32,9 +32,67 @@
 // TODO(112037636): Always include once fsverity.h is upstreamed.
 #if __has_include(<linux/fsverity.h>)
 #include <linux/fsverity.h>
-const int kSha256Bytes = 32;
+#else
+
+// Before fs-verity is upstreamed, use the current snapshot for development.
+// https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/tree/include/uapi/linux/fsverity.h?h=fsverity
+
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct fsverity_digest {
+    __u16 digest_algorithm;
+    __u16 digest_size; /* input/output */
+    __u8 digest[];
+};
+
+#define FS_IOC_ENABLE_VERITY	_IO('f', 133)
+#define FS_IOC_MEASURE_VERITY	_IOWR('f', 134, struct fsverity_digest)
+
+#define FS_VERITY_MAGIC		"FSVerity"
+
+#define FS_VERITY_ALG_SHA256	1
+
+struct fsverity_descriptor {
+    __u8 magic[8];		/* must be FS_VERITY_MAGIC */
+    __u8 major_version;	/* must be 1 */
+    __u8 minor_version;	/* must be 0 */
+    __u8 log_data_blocksize;/* log2(data-bytes-per-hash), e.g. 12 for 4KB */
+    __u8 log_tree_blocksize;/* log2(tree-bytes-per-hash), e.g. 12 for 4KB */
+    __le16 data_algorithm;	/* hash algorithm for data blocks */
+    __le16 tree_algorithm;	/* hash algorithm for tree blocks */
+    __le32 flags;		/* flags */
+    __le32 __reserved1;	/* must be 0 */
+    __le64 orig_file_size;	/* size of the original file data */
+    __le16 auth_ext_count;	/* number of authenticated extensions */
+    __u8 __reserved2[30];	/* must be 0 */
+};
+
+#define FS_VERITY_EXT_ROOT_HASH		1
+#define FS_VERITY_EXT_PKCS7_SIGNATURE	3
+
+struct fsverity_extension {
+    __le32 length;
+    __le16 type;		/* Type of this extension (see codes above) */
+    __le16 __reserved;	/* Reserved, must be 0 */
+};
+
+struct fsverity_digest_disk {
+    __le16 digest_algorithm;
+    __le16 digest_size;
+    __u8 digest[];
+};
+
+struct fsverity_footer {
+    __le32 desc_reverse_offset;	/* distance to fsverity_descriptor */
+    __u8 magic[8];			/* FS_VERITY_MAGIC */
+} __packed;
+
 #endif
 
+const int kSha256Bytes = 32;
+
 namespace android {
 
 namespace {
@@ -73,7 +131,6 @@
 };
 
 int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
-#if __has_include(<linux/fsverity.h>)
     const char* path = env->GetStringUTFChars(filePath, nullptr);
     ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
     if (rfd.get() < 0) {
@@ -83,14 +140,9 @@
       return errno;
     }
     return 0;
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return ENOSYS;
-#endif
 }
 
 int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
-#if __has_include(<linux/fsverity.h>)
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes);
     fsverity_digest* data = reinterpret_cast<fsverity_digest*>(raii->getRaw());
     data->digest_size = kSha256Bytes;  // the only input/output parameter
@@ -104,14 +156,9 @@
       return errno;
     }
     return 0;
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return ENOSYS;
-#endif
 }
 
 jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
-#if __has_include(<linux/fsverity.h>)
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
     fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii->getRaw());
 
@@ -126,15 +173,10 @@
     memcpy(data->digest, src, kSha256Bytes);
 
     return raii->release();
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return 0;
-#endif
 }
 
 
 jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
-#if __has_include(<linux/fsverity.h>)
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
     fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii->getRaw());
 
@@ -150,15 +192,10 @@
     desc->auth_ext_count = 1;
 
     return raii->release();
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return 0;
-#endif
 }
 
 jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId,
         jint extensionDataSize) {
-#if __has_include(<linux/fsverity.h>)
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension));
     fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii->getRaw());
 
@@ -166,15 +203,10 @@
     ext->type = extensionId;
 
     return raii->release();
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return 0;
-#endif
 }
 
 jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */,
         jint offsetToDescriptorHead) {
-#if __has_include(<linux/fsverity.h>)
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer));
     fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii->getRaw());
 
@@ -182,10 +214,6 @@
     memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic));
 
     return raii->release();
-#else
-    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
-    return 0;
-#endif
 }
 
 const JNINativeMethod sMethods[] = {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a01a026..8f5ce74 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8364,16 +8364,22 @@
                 != PackageManager.PERMISSION_GRANTED) {
             return false;
         }
-        // Allow access to the device owner.
+
+        // Allow access to the device owner or delegate cert installer.
         ComponentName deviceOwner = getDeviceOwnerComponent(true);
-        if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) {
+        if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
+                    || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
             return true;
         }
-        // Allow access to the profile owner for the specified user.
+        // Allow access to the profile owner for the specified user, or delegate cert installer
         ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
-        if (profileOwner != null && profileOwner.getPackageName().equals(packageName)) {
+        if (profileOwner != null && (profileOwner.getPackageName().equals(packageName)
+                    || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
             return true;
         }
+
+        Log.w(LOG_TAG, String.format("Package if %s (uid=%d, pid=%d) cannot access Device IDs",
+                    packageName, uid, pid));
         return false;
     }
 
@@ -14072,6 +14078,10 @@
 
         enforceCrossUsersPermission(userHandle);
         synchronized (getLockObject()) {
+            if (mInjector.settingsSecureGetIntForUser(
+                    Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, userHandle) == 0) {
+                return false;
+            }
             final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
             if (admin != null) {
                 if (admin.mCrossProfileCalendarPackages == null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d030fa5..71ed5ae 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -263,8 +263,6 @@
             "com.android.internal.car.CarServiceHelperService";
     private static final String TIME_DETECTOR_SERVICE_CLASS =
             "com.android.server.timedetector.TimeDetectorService$Lifecycle";
-    private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
-            "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
     private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
     private static final String ADB_SERVICE_CLASS =
@@ -827,6 +825,7 @@
     private void startOtherServices() {
         final Context context = mSystemContext;
         VibratorService vibrator = null;
+        DynamicAndroidService dynamicAndroid = null;
         IStorageManager storageManager = null;
         NetworkManagementService networkManagement = null;
         IpSecService ipSecService = null;
@@ -945,6 +944,11 @@
             ServiceManager.addService("vibrator", vibrator);
             traceEnd();
 
+            traceBeginAndSlog("StartDynamicAndroidService");
+            dynamicAndroid = new DynamicAndroidService(context);
+            ServiceManager.addService("dynamic_android", dynamicAndroid);
+            traceEnd();
+
             if (!isWatch) {
                 traceBeginAndSlog("StartConsumerIrService");
                 consumerIr = new ConsumerIrService(context);
@@ -1422,14 +1426,6 @@
                     reportWtf("starting StartTimeDetectorService service", e);
                 }
                 traceEnd();
-
-                traceBeginAndSlog("StartTimeZoneDetectorService");
-                try {
-                    mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting StartTimeZoneDetectorService service", e);
-                }
-                traceEnd();
             }
 
             if (!isWatch) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 5009d64..86e8598 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -16,6 +16,7 @@
 package com.android.server.power.batterysaver;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
@@ -178,8 +179,12 @@
                 .getSystemService(NotificationManager.class);
         doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
                 .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
+        doReturn(true).when(mMockBatterySaverController)
+                .setAdaptivePolicyLocked(any(BatterySaverPolicy.Policy.class), anyInt());
         when(mMockBatterySaverController.isEnabled())
                 .thenAnswer((inv) -> mDevice.batterySaverEnabled);
+        when(mMockBatterySaverController.isFullEnabled())
+                .thenAnswer((inv) -> mDevice.batterySaverEnabled);
         when(mMockResources.getBoolean(
                 com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
                 .thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java
new file mode 100644
index 0000000..1494284
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open 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.IDynamicAndroidService;
+import android.os.ServiceManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class DynamicAndroidServiceTest extends AndroidTestCase {
+    private static final String TAG = "DynamicAndroidServiceTests";
+    private IDynamicAndroidService mService;
+
+    @Override
+    protected void setUp() throws Exception {
+        mService =
+                IDynamicAndroidService.Stub.asInterface(
+                        ServiceManager.getService("dynamic_android"));
+    }
+
+    @LargeTest
+    public void test1() {
+        assertTrue("dynamic_android service available", mService != null);
+        try {
+            mService.startInstallation(1 << 20, 8 << 30);
+            fail("DynamicAndroidService did not throw SecurityException as expected");
+        } catch (SecurityException e) {
+            // expected
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 00a60b9..5dafe07 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -122,7 +122,7 @@
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
             long lastNetworkUpdatedProcStateSeq) {
-        final UidRecord record = new UidRecord(uid, null /* atmInternal */);
+        final UidRecord record = new UidRecord(uid);
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
         record.waitingForNetwork = true;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 419c736..4e21fd0c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -250,7 +250,7 @@
     }
 
     private UidRecord addUidRecord(int uid) {
-        final UidRecord uidRec = new UidRecord(uid, null /* atmInternal */);
+        final UidRecord uidRec = new UidRecord(uid);
         uidRec.waitingForNetwork = true;
         uidRec.hasInternetPermission = true;
         mAms.mProcessList.mActiveUids.put(uid, uidRec);
@@ -405,7 +405,7 @@
 
     @Test
     public void testBlockStateForUid() {
-        final UidRecord uidRec = new UidRecord(TEST_UID, null /* atmInternal */);
+        final UidRecord uidRec = new UidRecord(TEST_UID);
         int expectedBlockState;
 
         final String errorTemplate = "Block state should be %s, prevState: %s, curState: %s";
@@ -732,7 +732,7 @@
 
     @Test
     public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID, null /* atmInternal */);
+        final UidRecord uidRecord = new UidRecord(TEST_UID);
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
 
         // Verify with no pending changes for TEST_UID.
@@ -778,7 +778,7 @@
     @MediumTest
     @Test
     public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID, null /* atmInternal */);
+        final UidRecord uidRecord = new UidRecord(TEST_UID);
         final int expectedProcState = PROCESS_STATE_SERVICE;
         uidRecord.setProcState = expectedProcState;
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
@@ -850,7 +850,7 @@
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
             long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
-        final UidRecord record = new UidRecord(Process.myUid(), null /* atmInternal */);
+        final UidRecord record = new UidRecord(Process.myUid());
         record.curProcStateSeq = curProcStateSeq;
         record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
index 1a231cf..2f8e545 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
@@ -18,6 +18,7 @@
 
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
 import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
@@ -61,6 +62,9 @@
 @RunWith(AndroidJUnit4.class)
 public final class AppCompactorTest {
 
+    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
+            "device_config delete activity_manager";
+
     @Mock private AppOpsService mAppOpsService;
     private AppCompactor mCompactorUnderTest;
     private HandlerThread mHandlerThread;
@@ -70,19 +74,21 @@
     private static void clearDeviceConfig() throws IOException  {
         UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_USE_COMPACTION);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_ACTION_1);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_ACTION_2);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_1);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_2);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_3);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3);
         uiDevice.executeShellCommand(
-                "device_config delete activity_manager " + KEY_COMPACT_THROTTLE_4);
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4);
+        uiDevice.executeShellCommand(
+                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE);
     }
 
     @Before
@@ -128,6 +134,8 @@
                 is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
         assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
                 is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
+        assertThat(mCompactorUnderTest.mStatsdSampleRate,
+                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
     }
 
     @Test
@@ -155,6 +163,9 @@
         DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
                 KEY_COMPACT_THROTTLE_4,
                 Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
 
         // Then calling init will read and set that flag.
         mCompactorUnderTest.init();
@@ -173,6 +184,8 @@
                 is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
         assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
                 is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
+        assertThat(mCompactorUnderTest.mStatsdSampleRate,
+                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
     }
 
     @Test
@@ -365,6 +378,63 @@
                 is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
     }
 
+    @Test
+    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable values ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate,
+                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
+    }
+
+    @Test
+    public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable values ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate,
+                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
+    }
+
+    @Test
+    public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
+            throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with an value outside of [0..1]...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(-1.0f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+        // Then the values is capped in the range.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f));
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(1.01f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
+
+        // Then the values is capped in the range.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f));
+    }
+
     private class TestInjector extends Injector {
         @Override
         public AppOpsService getAppOpsService(File file, Handler handler) {
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index d4bb636..089a79b 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -194,6 +194,7 @@
                 false /* sticky */,
                 false /* initialSticky */,
                 userId,
-                false /* allowBackgroundActivityStarts */);
+                false, /* allowBackgroundActivityStarts */
+                false /* timeoutExempt */ );
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index a01d589..0355e84 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -1156,6 +1156,11 @@
     }
 
     @Override
+    public String getAppPredictionServicePackageName() {
+        return null;
+    }
+
+    @Override
     public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException {
         return false;
     }
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 535198b..9ac91dd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5278,6 +5278,35 @@
         assertTrue(actual.containsAll(expected));
     }
 
+    public void testIsPackageAllowedToAccessCalendar_adminNotAllowed() {
+        setAsProfileOwner(admin1);
+        dpm.setCrossProfileCalendarPackages(admin1, Collections.emptySet());
+        when(getServices().settings.settingsSecureGetIntForUser(
+                Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+                0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(1);
+        assertFalse(dpm.isPackageAllowedToAccessCalendar("TEST_PACKAGE"));
+    }
+
+    public void testIsPackageAllowedToAccessCalendar_settingOff() {
+        final String testPackage = "TEST_PACKAGE";
+        setAsProfileOwner(admin1);
+        dpm.setCrossProfileCalendarPackages(admin1, Collections.singleton(testPackage));
+        when(getServices().settings.settingsSecureGetIntForUser(
+                Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+                0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(0);
+        assertFalse(dpm.isPackageAllowedToAccessCalendar(testPackage));
+    }
+
+    public void testIsPackageAllowedToAccessCalendar_bothAllowed() {
+        final String testPackage = "TEST_PACKAGE";
+        setAsProfileOwner(admin1);
+        dpm.setCrossProfileCalendarPackages(admin1, null);
+        when(getServices().settings.settingsSecureGetIntForUser(
+                Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+                0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(1);
+        assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
+    }
+
     private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
         final long ident = mServiceContext.binder.clearCallingIdentity();
         mServiceContext.binder.callingUid =
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 79a654b..e3b1245 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -37,6 +37,9 @@
 import android.hardware.SensorEventListener;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -47,6 +50,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.AtomicFile;
+import android.view.Display;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -116,28 +120,81 @@
         assertTrue(mInjector.mIdleScheduled);
         mInjector.sendScreenChange(/*screen on */ true);
         assertNotNull(mInjector.mSensorListener);
+        assertTrue(mInjector.mColorSamplingEnabled);
 
         mInjector.sendScreenChange(/*screen on */ false);
         assertNull(mInjector.mSensorListener);
+        assertFalse(mInjector.mColorSamplingEnabled);
 
         // Turn screen on while brightness mode is manual
         mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ false);
         mInjector.sendScreenChange(/*screen on */ true);
         assertNull(mInjector.mSensorListener);
+        assertFalse(mInjector.mColorSamplingEnabled);
 
         // Set brightness mode to automatic while screen is off.
         mInjector.sendScreenChange(/*screen on */ false);
         mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ true);
         assertNull(mInjector.mSensorListener);
+        assertFalse(mInjector.mColorSamplingEnabled);
 
         // Turn on screen while brightness mode is automatic.
         mInjector.sendScreenChange(/*screen on */ true);
         assertNotNull(mInjector.mSensorListener);
+        assertTrue(mInjector.mColorSamplingEnabled);
 
         mTracker.stop();
         assertNull(mInjector.mSensorListener);
         assertNull(mInjector.mBroadcastReceiver);
         assertFalse(mInjector.mIdleScheduled);
+        assertFalse(mInjector.mColorSamplingEnabled);
+    }
+
+    @Test
+    public void testNoColorSampling_WrongPixelFormat() {
+        mInjector.mDefaultSamplingAttributes =
+                new DisplayedContentSamplingAttributes(
+                        0x23,
+                        mInjector.mDefaultSamplingAttributes.getDataspace(),
+                        mInjector.mDefaultSamplingAttributes.getComponentMask());
+        startTracker(mTracker);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
+    }
+
+    @Test
+    public void testNoColorSampling_MissingComponent() {
+        mInjector.mDefaultSamplingAttributes =
+                new DisplayedContentSamplingAttributes(
+                        mInjector.mDefaultSamplingAttributes.getPixelFormat(),
+                        mInjector.mDefaultSamplingAttributes.getDataspace(),
+                        0x2);
+        startTracker(mTracker);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
+    }
+
+    @Test
+    public void testNoColorSampling_NoSupport() {
+        mInjector.mDefaultSamplingAttributes = null;
+        startTracker(mTracker);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
+    }
+
+    @Test
+    public void testColorSampling_FrameRateChange() {
+        startTracker(mTracker);
+        assertTrue(mInjector.mColorSamplingEnabled);
+        assertNotNull(mInjector.mDisplayListener);
+        int noFramesSampled = mInjector.mNoColorSamplingFrames;
+        mInjector.mFrameRate = 120.0f;
+        // Wrong display
+        mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY + 10);
+        assertEquals(noFramesSampled, mInjector.mNoColorSamplingFrames);
+        // Correct display
+        mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
+        assertEquals(noFramesSampled * 2, mInjector.mNoColorSamplingFrames);
     }
 
     @Test
@@ -149,26 +206,41 @@
         assertNotNull(mInjector.mBroadcastReceiver);
         assertNotNull(mInjector.mContentObserver);
         assertTrue(mInjector.mIdleScheduled);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
 
         mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
         assertNotNull(mInjector.mSensorListener);
+        assertTrue(mInjector.mColorSamplingEnabled);
+        assertNotNull(mInjector.mDisplayListener);
 
         SensorEventListener listener = mInjector.mSensorListener;
+        DisplayManager.DisplayListener displayListener = mInjector.mDisplayListener;
         mInjector.mSensorListener = null;
+        mInjector.mColorSamplingEnabled = false;
+        mInjector.mDisplayListener = null;
         // Duplicate notification
         mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
         // Sensor shouldn't have been registered as it was already registered.
         assertNull(mInjector.mSensorListener);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
         mInjector.mSensorListener = listener;
+        mInjector.mDisplayListener = displayListener;
+        mInjector.mColorSamplingEnabled = true;
 
         mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false);
         assertNull(mInjector.mSensorListener);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
 
         mTracker.stop();
         assertNull(mInjector.mSensorListener);
         assertNull(mInjector.mBroadcastReceiver);
         assertNull(mInjector.mContentObserver);
         assertFalse(mInjector.mIdleScheduled);
+        assertFalse(mInjector.mColorSamplingEnabled);
+        assertNull(mInjector.mDisplayListener);
     }
 
     @Test
@@ -229,6 +301,8 @@
         assertEquals(3333, event.colorTemperature);
         assertEquals("a.package", event.packageName);
         assertEquals(0, event.userId);
+        assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
+        assertEquals(10000, event.colorSampleDuration);
 
         assertEquals(1, eventsNoPackage.size());
         assertNull(eventsNoPackage.get(0).packageName);
@@ -342,7 +416,8 @@
                 + "lastNits=\"32\" "
                 + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n"
                 + "lux=\"132.2,131.1\" luxTimestamps=\""
-                + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+                + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+                + "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>"
                 // Event that is too old so shouldn't show up.
                 + "<event nits=\"142\" timestamp=\""
                 + Long.toString(twoMonthsAgo) + "\" packageName=\""
@@ -368,6 +443,7 @@
         assertTrue(event.isDefaultBrightnessConfig);
         assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
         assertTrue(event.isUserSetBrightness);
+        assertNull(event.colorValueBuckets);
 
         events = tracker.getEvents(1, true).getList();
         assertEquals(1, events.size());
@@ -386,6 +462,8 @@
         assertFalse(event.isDefaultBrightnessConfig);
         assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
         assertFalse(event.isUserSetBrightness);
+        assertEquals(3456L, event.colorSampleDuration);
+        assertArrayEquals(new long[] {123L, 598L, 23L, 19L}, event.colorValueBuckets);
 
         // Pretend user 1 is a profile of user 0.
         mInjector.mProfiles = new int[]{0, 1};
@@ -481,6 +559,8 @@
         assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
         assertTrue(event.isUserSetBrightness);
         assertFalse(event.isDefaultBrightnessConfig);
+        assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
+        assertEquals(10000, event.colorSampleDuration);
     }
 
     @Test
@@ -546,6 +626,7 @@
         builder.setNightMode(false);
         builder.setColorTemperature(345);
         builder.setLastBrightness(50f);
+        builder.setColorValues(new long[] {23, 34, 45}, 1000L);
         BrightnessChangeEvent event = builder.build();
 
         event.writeToParcel(parcel, 0);
@@ -568,6 +649,8 @@
         assertEquals(event.nightMode, event2.nightMode);
         assertEquals(event.colorTemperature, event2.colorTemperature);
         assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
+        assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
+        assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
 
         parcel = Parcel.obtain();
         builder.setBatteryLevel(Float.NaN);
@@ -714,6 +797,7 @@
     private class TestInjector extends BrightnessTracker.Injector {
         SensorEventListener mSensorListener;
         BroadcastReceiver mBroadcastReceiver;
+        DisplayManager.DisplayListener mDisplayListener;
         Map<String, Integer> mSecureIntSettings = new HashMap<>();
         long mCurrentTimeMillis = System.currentTimeMillis();
         long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -723,6 +807,12 @@
         int[] mProfiles;
         ContentObserver mContentObserver;
         boolean mIsBrightnessModeAutomatic = true;
+        boolean mColorSamplingEnabled = false;
+        DisplayedContentSamplingAttributes mDefaultSamplingAttributes =
+                new DisplayedContentSamplingAttributes(0x37, 0, 0x4);
+        float mFrameRate = 60.0f;
+        int mNoColorSamplingFrames;
+
 
         public TestInjector(Handler handler) {
             mHandler = handler;
@@ -882,5 +972,43 @@
             return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
                     0) == 1;
         }
+
+        @Override
+        public DisplayedContentSample sampleColor(int noFramesToSample) {
+            return new DisplayedContentSample(600L,
+                    null,
+                    null,
+                     new long[] {1, 10, 100, 1000, 300, 30, 10, 1},
+                    null);
+        }
+
+        @Override
+        public float getFrameRate(Context context) {
+            return mFrameRate;
+        }
+
+        @Override
+        public DisplayedContentSamplingAttributes getSamplingAttributes() {
+            return mDefaultSamplingAttributes;
+        }
+
+        @Override
+        public boolean enableColorSampling(boolean enable, int noFrames) {
+            mColorSamplingEnabled = enable;
+            mNoColorSamplingFrames = noFrames;
+            return true;
+        }
+
+        @Override
+        public void registerDisplayListener(Context context,
+                DisplayManager.DisplayListener listener, Handler handler) {
+            mDisplayListener = listener;
+        }
+
+        @Override
+        public void unRegisterDisplayListener(Context context,
+                DisplayManager.DisplayListener listener) {
+            mDisplayListener = null;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 0dff03f..bfa0b74 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,7 +21,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -86,7 +85,7 @@
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
         when(mBatterySaverPolicyMock.getBatterySaverPolicy(
-                eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
+                eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
                 .thenReturn(mPowerSaveState);
 
         mDisplayPowerRequest = new DisplayPowerRequest();
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 73eefcf..e32a789 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -15,9 +15,11 @@
  */
 package com.android.server.power.batterysaver;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.server.power.batterysaver.BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE;
+import static com.android.server.power.batterysaver.BatterySaverPolicy.POLICY_LEVEL_FULL;
+import static com.android.server.power.batterysaver.BatterySaverPolicy.POLICY_LEVEL_OFF;
 
-import static org.mockito.Mockito.mock;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
 import android.os.PowerManager;
@@ -30,24 +32,23 @@
 
 import com.android.frameworks.servicestests.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
+import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
 
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
  * Tests for {@link com.android.server.power.batterysaver.BatterySaverPolicy}
  */
 public class BatterySaverPolicyTest extends AndroidTestCase {
-    private static final boolean BATTERY_SAVER_ON = true;
-    private static final boolean BATTERY_SAVER_OFF = false;
+    private static final int MAX_SERVICE_TYPE = 15;
     private static final float BRIGHTNESS_FACTOR = 0.7f;
     private static final float DEFAULT_BRIGHTNESS_FACTOR = 0.5f;
     private static final float PRECISION = 0.001f;
-    private static final int GPS_MODE = 0;
+    private static final int GPS_MODE = 0; // LOCATION_MODE_NO_CHANGE
     private static final int DEFAULT_GPS_MODE =
             PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
     private static final String BATTERY_SAVER_CONSTANTS = "vibration_disabled=true,"
+            + "advertise_is_enabled=true,"
             + "animation_disabled=false,"
             + "soundtrigger_disabled=true,"
             + "firewall_disabled=false,"
@@ -56,7 +57,7 @@
             + "adjust_brightness_factor=0.7,"
             + "fullbackup_deferred=true,"
             + "keyvaluebackup_deferred=false,"
-            + "gps_mode=0,"
+            + "gps_mode=0," // LOCATION_MODE_NO_CHANGE
             + "quick_doze_enabled=true";
     private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true";
 
@@ -82,9 +83,6 @@
         }
     }
 
-    @Mock
-    MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
-
     private BatterySaverPolicyForTest mBatterySaverPolicy;
 
     private final ArrayMap<String, String> mMockGlobalSettings = new ArrayMap<>();
@@ -95,8 +93,10 @@
         MockitoAnnotations.initMocks(this);
         final Object lock = new Object();
         mBatterySaverPolicy = new BatterySaverPolicyForTest(lock, getContext(),
-                new BatterySavingStats(lock, mMetricsLogger));
+                new BatterySavingStats(lock));
         mBatterySaverPolicy.systemReady();
+
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
     }
 
     @SmallTest
@@ -148,12 +148,14 @@
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyDataSaver_DefaultValueCorrect() {
         mBatterySaverPolicy.updateConstantsLocked("", "");
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
         final PowerSaveState batterySaverStateOn =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.DATA_SAVER, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.DATA_SAVER);
         assertThat(batterySaverStateOn.batterySaverEnabled).isFalse();
 
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
         final PowerSaveState batterySaverStateOff = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.DATA_SAVER, BATTERY_SAVER_OFF);
+                ServiceType.DATA_SAVER);
         assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
     }
 
@@ -166,8 +168,9 @@
     public void testGetBatterySaverPolicy_PolicyGps_DefaultValueCorrect() {
         testServiceDefaultValue_On(ServiceType.GPS);
 
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
         PowerSaveState stateOn =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, true);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS);
         assertThat(stateOn.gpsMode).isEqualTo(DEFAULT_GPS_MODE);
     }
 
@@ -180,47 +183,51 @@
     public void testUpdateConstants_getCorrectData() {
         mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, "");
 
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
+        verifyBatterySaverConstantsUpdated();
+    }
+
+    private void verifyBatterySaverConstantsUpdated() {
         final PowerSaveState vibrationState =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.VIBRATION, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.VIBRATION);
         assertThat(vibrationState.batterySaverEnabled).isTrue();
 
         final PowerSaveState animationState =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.ANIMATION, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.ANIMATION);
         assertThat(animationState.batterySaverEnabled).isFalse();
 
         final PowerSaveState soundState =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SOUND, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SOUND);
         assertThat(soundState.batterySaverEnabled).isTrue();
 
         final PowerSaveState networkState = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.NETWORK_FIREWALL, BATTERY_SAVER_ON);
+                ServiceType.NETWORK_FIREWALL);
         assertThat(networkState.batterySaverEnabled).isTrue();
 
         final PowerSaveState screenState =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS,
-                        BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS);
         assertThat(screenState.batterySaverEnabled).isFalse();
         assertThat(screenState.brightnessFactor).isWithin(PRECISION).of(BRIGHTNESS_FACTOR);
 
         final PowerSaveState fullBackupState = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.FULL_BACKUP, BATTERY_SAVER_ON);
+                ServiceType.FULL_BACKUP);
         assertThat(fullBackupState.batterySaverEnabled).isTrue();
 
         final PowerSaveState keyValueBackupState = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.KEYVALUE_BACKUP, BATTERY_SAVER_ON);
+                ServiceType.KEYVALUE_BACKUP);
         assertThat(keyValueBackupState.batterySaverEnabled).isFalse();
 
         final PowerSaveState dataSaverState = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.DATA_SAVER, BATTERY_SAVER_ON);
+                ServiceType.DATA_SAVER);
         assertThat(dataSaverState.batterySaverEnabled).isTrue();
 
         final PowerSaveState gpsState =
-                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS);
         assertThat(gpsState.batterySaverEnabled).isTrue();
         assertThat(gpsState.gpsMode).isEqualTo(GPS_MODE);
 
         final PowerSaveState quickDozeState = mBatterySaverPolicy.getBatterySaverPolicy(
-                ServiceType.QUICK_DOZE, BATTERY_SAVER_ON);
+                ServiceType.QUICK_DOZE);
         assertThat(quickDozeState.batterySaverEnabled).isTrue();
     }
 
@@ -233,23 +240,27 @@
 
     private void testServiceDefaultValue_On(@ServiceType int type) {
         mBatterySaverPolicy.updateConstantsLocked("", "");
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
         final PowerSaveState batterySaverStateOn =
-                mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(type);
         assertThat(batterySaverStateOn.batterySaverEnabled).isTrue();
 
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
         final PowerSaveState batterySaverStateOff =
-                mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_OFF);
+                mBatterySaverPolicy.getBatterySaverPolicy(type);
         assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
     }
 
     private void testServiceDefaultValue_Off(@ServiceType int type) {
         mBatterySaverPolicy.updateConstantsLocked("", "");
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
         final PowerSaveState batterySaverStateOn =
-                mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON);
+                mBatterySaverPolicy.getBatterySaverPolicy(type);
         assertThat(batterySaverStateOn.batterySaverEnabled).isFalse();
 
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
         final PowerSaveState batterySaverStateOff =
-                mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_OFF);
+                mBatterySaverPolicy.getBatterySaverPolicy(type);
         assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
     }
 
@@ -291,4 +302,26 @@
                         + "/sys/devices/system/cpu/cpu5/cpufreq/scaling_max_freq=15}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
     }
+
+    public void testSetPolicyLevel_Off() {
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
+
+        // +1 to make sure the default value is off as well.
+        for (int i = 0; i < MAX_SERVICE_TYPE + 1; ++i) {
+            assertThat(mBatterySaverPolicy.getBatterySaverPolicy(i).batterySaverEnabled).isFalse();
+        }
+    }
+
+    public void testSetPolicyLevel_Adaptive() {
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
+
+        mBatterySaverPolicy.setAdaptivePolicyLocked(BatterySaverPolicy.OFF_POLICY);
+        for (int i = 0; i < MAX_SERVICE_TYPE + 1; ++i) {
+            assertThat(mBatterySaverPolicy.getBatterySaverPolicy(i).batterySaverEnabled).isFalse();
+        }
+
+        mBatterySaverPolicy.setAdaptivePolicyLocked(
+                Policy.fromSettings(BATTERY_SAVER_CONSTANTS, ""));
+        verifyBatterySaverConstantsUpdated();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index ba61fd2..76239fc 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -30,14 +30,12 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
 import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
@@ -54,7 +52,7 @@
         private int mBatteryLevel = 1_000_000_000;
 
         private BatterySavingStatsTestable() {
-            super(new Object(), mMetricsLogger);
+            super(new Object());
         }
 
         @Override
@@ -104,23 +102,13 @@
 
     public MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
 
-    private boolean sendTronEvents;
-
     @Test
-    public void testAll_withTron() {
-        sendTronEvents = true;
-        checkAll();
-    }
-
-    @Test
-    public void testAll_noTron() {
-        sendTronEvents = false;
+    public void testAll() {
         checkAll();
     }
 
     private void checkAll() {
         final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
-        target.setSendTronLog(sendTronEvents);
 
         target.assertDumpable();
 
@@ -240,46 +228,13 @@
                 target.toDebugString());
     }
 
-    private void assertLog(boolean batterySaver, boolean interactive, long deltaTimeMs,
-            int deltaBatteryLevelUa, int deltaBatteryLevelPercent) {
-        if (sendTronEvents) {
-            ArgumentCaptor<LogMaker> ac = ArgumentCaptor.forClass(LogMaker.class);
-            verify(mMetricsLogger, times(1)).write(ac.capture());
-
-            LogMaker lm = ac.getValue();
-            assertEquals(MetricsEvent.BATTERY_SAVER, lm.getCategory());
-            assertEquals(batterySaver ? 1 : 0,
-                    lm.getTaggedData(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE));
-            assertEquals(interactive ? 1 : 0, lm.getTaggedData(MetricsEvent.FIELD_INTERACTIVE));
-            assertEquals(deltaTimeMs, lm.getTaggedData(MetricsEvent.FIELD_DURATION_MILLIS));
-
-            assertEquals(deltaBatteryLevelUa,
-                    (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_UA)
-                            - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_UA));
-            assertEquals(deltaBatteryLevelPercent,
-                    (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT)
-                            - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT));
-        } else {
-            verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
-        }
-    }
-
-
-    @Test
-    public void testMetricsLogger_withTron() {
-        sendTronEvents = true;
-        checkMetricsLogger();
+    private void assertLog() {
+        verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
     }
 
     @Test
-    public void testMetricsLogger_noTron() {
-        sendTronEvents = false;
-        checkMetricsLogger();
-    }
-
-    private void checkMetricsLogger() {
+    public void testMetricsLogger() {
         final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
-        target.setSendTronLog(sendTronEvents);
 
         target.advanceClock(1);
         target.drainBattery(1000);
@@ -300,7 +255,7 @@
                 InteractiveState.NON_INTERACTIVE,
                 DozeState.NOT_DOZING);
 
-        assertLog(false, true, 60_000, 2000, 200);
+        assertLog();
 
         target.advanceClock(1);
         target.drainBattery(2000);
@@ -331,7 +286,7 @@
                 InteractiveState.INTERACTIVE,
                 DozeState.NOT_DOZING);
 
-        assertLog(false, false, 60_000 * 3, 2000 * 3, 200 * 3);
+        assertLog();
 
         target.advanceClock(10);
         target.drainBattery(10000);
@@ -339,7 +294,7 @@
         reset(mMetricsLogger);
         target.startCharging();
 
-        assertLog(true, true, 60_000 * 10, 10000, 1000);
+        assertLog();
 
         target.advanceClock(1);
         target.drainBattery(2000);
@@ -357,6 +312,6 @@
 
         target.startCharging();
 
-        assertLog(true, false, 60_000, 2000, 200);
+        assertLog();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
deleted file mode 100644
index 0936fff..0000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timezonedetector;
-
-import android.content.Context;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Unit tests for the {@link TimeZoneDetectorService}.
- */
-@RunWith(AndroidJUnit4.class)
-public class TimeZoneDetectorServiceTest {
-
-    private TimeZoneDetectorService mTimeZoneDetectorService;
-
-    @Before
-    public void setUp() {
-        final Context context = InstrumentationRegistry.getContext();
-        mTimeZoneDetectorService = new TimeZoneDetectorService(context);
-    }
-
-    @Test
-    public void testStubbedCall() {
-        mTimeZoneDetectorService.stubbedCall();
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 319ffed..f1506a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -368,4 +368,15 @@
         verify(mService.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()),
                 eq(mActivity.appToken), eq(expected));
     }
+
+    @Test
+    public void testShouldMakeActive_deferredResume() {
+        mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+
+        mSupervisor.beginDeferResume();
+        assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+
+        mSupervisor.endDeferResume();
+        assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index ace965b..60f1ae26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -61,6 +61,7 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -561,7 +562,7 @@
         runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
     }
 
     /**
@@ -576,7 +577,7 @@
                 "disallowed_unsupportedUsecase_aborted", true,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
     }
 
     /**
@@ -591,57 +592,70 @@
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
                 Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
                 Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false,
                 Process.NFC_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
-                false, false, false);
+                false, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_hasForegroundActivities_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                true, false, false);
+                true, false, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callerIsRecents_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, true, false);
+                false, true, false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callerIsWhitelisted_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false, true);
+                false, false, true, false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callerIsInstrumentingWithBackgroundActivityStartPrivileges_notAborted",
+                false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false, false, true, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callingPackageIsDeviceOwner_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false, false, false, true);
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
             int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
             int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
             boolean hasForegroundActivities, boolean callerIsRecents,
-            boolean callerIsTempWhitelisted) {
+            boolean callerIsTempWhitelisted,
+            boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+            boolean isCallingPackageDeviceOwner) {
         // window visibility
         doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
                 .isAnyNonToastWindowVisibleForUid(callingUid);
@@ -664,6 +678,12 @@
         doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
         // caller is temp whitelisted
         callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
+        // caller is instrumenting with background activity starts privileges
+        callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+                callerIsInstrumentingWithBackgroundActivityStartPrivileges);
+        // caller is device owner
+        DevicePolicyManager dpmMock = mService.getDevicePolicyManager();
+        doReturn(isCallingPackageDeviceOwner).when(dpmMock).isDeviceOwnerApp(any());
 
         final ActivityOptions options = spy(ActivityOptions.makeBasic());
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 3b399ff..e27dd94 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -42,6 +42,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -424,6 +425,7 @@
             spyOn(getLifecycleManager());
             spyOn(getLockTaskController());
             doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            doReturn(mock(DevicePolicyManager.class)).when(this).getDevicePolicyManager();
             // allow background activity starts by default
             doReturn(true).when(this).isBackgroundActivityStartsEnabled();
             doNothing().when(this).updateCpuStats();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 19ace3c..3bf884f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -55,7 +55,7 @@
         mStack = createTaskStackOnDisplay(mDisplayContent);
         mTask = createTaskInStack(mStack, 0 /* userId */);
         mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
-        mToken.mSkipOnParentSet = false;
+        mToken.mSkipOnParentChanged = false;
 
         mTask.addChild(mToken, 0);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index e156143..8628575 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -103,13 +103,13 @@
         assertEquals(secondLayer1, root.getChildAt(5));
         assertEquals(layer2, root.getChildAt(6));
 
-        assertTrue(layer1.mOnParentSetCalled);
-        assertTrue(secondLayer1.mOnParentSetCalled);
-        assertTrue(layer2.mOnParentSetCalled);
-        assertTrue(layerNeg1.mOnParentSetCalled);
-        assertTrue(layerNeg2.mOnParentSetCalled);
-        assertTrue(secondLayerNeg1.mOnParentSetCalled);
-        assertTrue(layer0.mOnParentSetCalled);
+        assertTrue(layer1.mOnParentChangedCalled);
+        assertTrue(secondLayer1.mOnParentChangedCalled);
+        assertTrue(layer2.mOnParentChangedCalled);
+        assertTrue(layerNeg1.mOnParentChangedCalled);
+        assertTrue(layerNeg2.mOnParentChangedCalled);
+        assertTrue(secondLayerNeg1.mOnParentChangedCalled);
+        assertTrue(layer0.mOnParentChangedCalled);
     }
 
     @Test
@@ -747,7 +747,7 @@
         private boolean mFillsParent;
         private Integer mOrientation;
 
-        private boolean mOnParentSetCalled;
+        private boolean mOnParentChangedCalled;
         private boolean mOnDescendantOverrideCalled;
 
         /**
@@ -801,8 +801,8 @@
         }
 
         @Override
-        void onParentSet() {
-            mOnParentSetCalled = true;
+        void onParentChanged() {
+            mOnParentChangedCalled = true;
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 2263cf3..a494889 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -152,7 +152,7 @@
     public static class TestAppWindowToken extends AppWindowToken {
         boolean mOnTop = false;
         private Transaction mPendingTransactionOverride;
-        boolean mSkipOnParentSet = true;
+        boolean mSkipOnParentChanged = true;
 
         private TestAppWindowToken(DisplayContent dc) {
             super(dc.mWmService, new IApplicationToken.Stub() {
@@ -200,9 +200,11 @@
         }
 
         @Override
-        void onParentSet() {
-            if (!mSkipOnParentSet) {
-                super.onParentSet();
+        void onParentChanged() {
+            if (!mSkipOnParentChanged) {
+                super.onParentChanged();
+            } else {
+                updateConfigurationFromParent(this);
             }
         }
 
@@ -223,6 +225,21 @@
         }
     }
 
+    /**
+     * Used when we don't want to perform surface related operation in
+     * {@link WindowContainer#onParentChanged} or the overridden method, but the configuration
+     * still needs to propagate from parent.
+     *
+     * @see ConfigurationContainer#onParentChanged
+     */
+    static void updateConfigurationFromParent(WindowContainer container) {
+        final WindowContainer parent = container.getParent();
+        if (parent != null) {
+            container.onConfigurationChanged(parent.getConfiguration());
+            container.onMergedOverrideConfigurationChanged();
+        }
+    }
+
     static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
         return createTestWindowToken(type, dc, false /* persistOnEmpty */);
     }
@@ -341,8 +358,8 @@
         }
 
         @Override
-        void onParentSet() {
-            // Do nothing;
+        void onParentChanged() {
+            updateConfigurationFromParent(this);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e38118e..3cb2814 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -91,6 +91,12 @@
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
 
+    /**
+     * To restore the original SurfaceControl.Transaction factory if any tests changed
+     * {@link WindowManagerService#mTransactionFactory}.
+     */
+    private TransactionFactory mOriginalTransactionFactory;
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
@@ -121,6 +127,7 @@
             final Context context = getInstrumentation().getTargetContext();
 
             mWm = TestSystemServices.getWindowManagerService();
+            mOriginalTransactionFactory = mWm.mTransactionFactory;
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -170,6 +177,7 @@
             // stable state to clean up for consistency.
             waitUntilHandlersIdle();
 
+            mWm.mTransactionFactory = mOriginalTransactionFactory;
             final LinkedList<WindowState> nonCommonWindows = new LinkedList<>();
 
             synchronized (mWm.mGlobalLock) {
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/DISABLED_TEST_MAPPING
similarity index 100%
rename from startop/iorap/TEST_MAPPING
rename to startop/iorap/DISABLED_TEST_MAPPING
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 9a30b35..22fc159 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -50,8 +50,8 @@
     public static final String TAG = "IorapForwardingService";
     /** $> adb shell 'setprop log.tag.IorapdForwardingService VERBOSE' */
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    /** $> adb shell 'setprop iorapd.enable true' */
-    private static boolean IS_ENABLED = SystemProperties.getBoolean("iorapd.enable", true);
+    /** $> adb shell 'setprop ro.iorapd.enable true' */
+    private static boolean IS_ENABLED = SystemProperties.getBoolean("ro.iorapd.enable", true);
     /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */
     private static boolean WTF_CRASH = SystemProperties.getBoolean(
             "iorapd.forwarding_service.wtf_crash", false);
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 7006075..5f7d3f9 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -4,6 +4,14 @@
       "name": "dex-builder-test"
     },
     {
+      "name": "CtsViewTestCases",
+      "options": [
+        {
+          "include-filter": "android.view.cts.PrecompiledLayoutTest"
+        }
+      ]
+    },
+    {
       "name": "view-compiler-tests",
       "host": true
     }
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index b2fa5d4..ef78ea5 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -22,6 +22,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -162,7 +163,9 @@
 
         final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
         dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
-        return filterByIntent(context, packageNames, dialIntentWithTelScheme, userId);
+        packageNames = filterByIntent(context, packageNames, dialIntentWithTelScheme, userId);
+        packageNames = requireInCallService(packageNames, userId, context);
+        return packageNames;
     }
 
     public static List<String> getInstalledDialerApplications(Context context) {
@@ -220,6 +223,35 @@
         return result;
     }
 
+    private static List<String> requireInCallService(List<String> packageNames, int userId,
+            Context context) {
+        if (packageNames == null || packageNames.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        final Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
+        final List<ResolveInfo> resolveInfoList = context.getPackageManager()
+                .queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, userId);
+        final List<String> result = new ArrayList<>();
+        final int length = resolveInfoList.size();
+        for (int i = 0; i < length; i++) {
+            final ServiceInfo info = resolveInfoList.get(i).serviceInfo;
+            if (info == null || info.metaData == null) {
+                continue;
+            }
+            if (!info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_UI)) {
+                continue;
+            }
+            if (info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI)) {
+                continue;
+            }
+            if (packageNames.contains(info.packageName) && !result.contains(info.packageName)) {
+                result.add(info.packageName);
+            }
+        }
+
+        return result;
+    }
 
     private static TelecomManager getTelecomManager(Context context) {
         return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 2d29875..a4cce9c 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -50,10 +50,9 @@
     }
 
     private CallAttributes(Parcel in) {
-        mPreciseCallState = (PreciseCallState)
-                in.readValue(PreciseCallState.class.getClassLoader());
-        mNetworkType = in.readInt();
-        mCallQuality = (CallQuality) in.readValue(CallQuality.class.getClassLoader());
+        this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader());
+        this.mNetworkType = in.readInt();
+        this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader());
     }
 
     // getters
@@ -134,9 +133,9 @@
      * {@link Parcelable#writeToParcel}
      */
     public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
-        mPreciseCallState.writeToParcel(dest, flags);
+        dest.writeParcelable(mPreciseCallState, flags);
         dest.writeInt(mNetworkType);
-        mCallQuality.writeToParcel(dest, flags);
+        dest.writeParcelable(mCallQuality, flags);
     }
 
     public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 4bca404..6c45cc4 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -112,13 +113,13 @@
                     mSlotId, 0, null).sendToTarget();
         }
 
-        private void registerForStateChanged(INetworkServiceCallback callback) {
+        private void registerForStateChanged(@NonNull INetworkServiceCallback callback) {
             synchronized (mNetworkRegistrationStateChangedCallbacks) {
                 mNetworkRegistrationStateChangedCallbacks.add(callback);
             }
         }
 
-        private void unregisterForStateChanged(INetworkServiceCallback callback) {
+        private void unregisterForStateChanged(@NonNull INetworkServiceCallback callback) {
             synchronized (mNetworkRegistrationStateChangedCallbacks) {
                 mNetworkRegistrationStateChangedCallbacks.remove(callback);
             }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 869cf1c..dfe36ef 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2577,8 +2577,14 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setPreferredDataSubscriptionId(int subId) {
         if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
-        setSubscriptionPropertyHelper(DEFAULT_SUBSCRIPTION_ID, "setPreferredDataSubscriptionId",
-                (iSub)-> iSub.setPreferredDataSubscriptionId(subId));
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                iSub.setPreferredDataSubscriptionId(subId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index dfdec4d..7c3bde4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -72,6 +72,7 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
@@ -353,27 +354,26 @@
      * Returns 3 for Tri standby mode.(Tri SIM functionality)
      */
     public int getPhoneCount() {
-        int phoneCount = 0;
-
-        // check for voice and data support, 0 if not supported
-        if (!isVoiceCapable() && !isSmsCapable()) {
-            ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
-                    Context.CONNECTIVITY_SERVICE);
-            if (cm != null) {
-                if (!cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
-                    return phoneCount;
+        int phoneCount = 1;
+        switch (getMultiSimConfiguration()) {
+            case UNKNOWN:
+                ConnectivityManager cm = mContext == null ? null : (ConnectivityManager) mContext
+                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+                // check for voice and data support, 0 if not supported
+                if (!isVoiceCapable() && !isSmsCapable() && cm != null
+                        && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+                    phoneCount = 0;
+                } else {
+                    phoneCount = 1;
                 }
-            }
-        }
-
-        phoneCount = 1;
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                phoneCount = telephony.getNumOfActiveSims();
-            }
-        } catch (RemoteException ex) {
-            Rlog.e(TAG, "getNumOfActiveSims RemoteException", ex);
+                break;
+            case DSDS:
+            case DSDA:
+                phoneCount = PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+                break;
+            case TSTS:
+                phoneCount = PhoneConstants.MAX_PHONE_COUNT_TRI_SIM;
+                break;
         }
         return phoneCount;
     }
@@ -3264,6 +3264,35 @@
         }
     }
 
+    /**
+     * Get the mapping from logical slots to physical slots. The mapping represent by a pair list.
+     * The key of the piar is the logical slot id and the value of the pair is the physical
+     * slots id mapped to this logical slot id.
+     *
+     * @return an pair list indicates the mapping from logical slots to physical slots. The size of
+     * the list should be {@link #getPhoneCount()} if success, otherwise return an empty list.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull
+    public List<Pair<Integer, Integer>> getLogicalToPhysicalSlotMapping() {
+        List<Pair<Integer, Integer>> slotMapping = new ArrayList<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] slotMappingArray = telephony.getSlotsMapping();
+                for (int i = 0; i < slotMappingArray.length; i++) {
+                    slotMapping.add(new Pair(i, slotMappingArray[i]));
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getSlotsMapping RemoteException", e);
+        }
+        return slotMapping;
+    }
+
     //
     //
     // Subscriber Info
@@ -10239,4 +10268,24 @@
             Rlog.e(TAG, "switchMultiSimConfig RemoteException", ex);
         }
     }
+
+    /**
+     * Get whether reboot is required or not after making changes to modem configurations.
+     * @Return {@code True} if reboot is required after making changes to modem configurations,
+     * otherwise return {@code False}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isRebootRequiredForModemConfigChange() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isRebootRequiredForModemConfigChange();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "isRebootRequiredForModemConfigChange RemoteException", e);
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 74d1e83..79572b9 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -157,7 +157,10 @@
                                   @Nullable LinkProperties linkProperties,
                                   @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
-            callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+            if (callback != null) {
+                callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED,
+                        null);
+            }
         }
 
         /**
@@ -176,7 +179,9 @@
         public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
                                        @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
-            callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            if (callback != null) {
+                callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
         }
 
         /**
@@ -190,7 +195,10 @@
         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
                                         @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
-            callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            if (callback != null) {
+                callback.onSetInitialAttachApnComplete(
+                        DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
         }
 
         /**
@@ -206,7 +214,9 @@
         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
                                    @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
-            callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            if (callback != null) {
+                callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
         }
 
         /**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index bca088e..1bbf9f4 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -408,7 +408,7 @@
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
     private final Context mContext;
-    private final int mCardId;
+    private int mCardId;
 
     /** @hide */
     public EuiccManager(Context context) {
@@ -446,7 +446,7 @@
     public boolean isEnabled() {
         // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
         // restrictions.
-        return getIEuiccController() != null && mCardId != TelephonyManager.INVALID_CARD_ID;
+        return getIEuiccController() != null;
     }
 
     /**
@@ -456,15 +456,15 @@
      * current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
      * access to the EID of another eUICC.
      *
-     * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
+     * @return the EID. May be null if the eUICC is not ready.
      */
     @Nullable
     public String getEid() {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             return null;
         }
         try {
-            return getIEuiccController().getEid(mCardId);
+            return getIEuiccController().getEid(mCardId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -475,15 +475,15 @@
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
-     * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
-     *     {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+     * @return the status of eUICC OTA. If the eUICC is not ready,
+     *         {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
         }
         try {
@@ -502,6 +502,15 @@
      * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
      * returned in the callback intent to prompt the user to accept the download.
      *
+     * <p>On a multi-active SIM device, requires the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+     * only if the targeted eUICC does not currently have an active subscription or the calling app
+     * is authorized to manage the active subscription on the target eUICC, and the calling app is
+     * authorized to manage any active subscription on any SIM. Without it, an
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+     * intent to prompt the user to accept the download. The caller should also be authorized to
+     * manage the subscription to be downloaded.
+     *
      * @param subscription the subscription to download.
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
@@ -509,7 +518,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -571,7 +580,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             PendingIntent callbackIntent =
                     resolutionIntent.getParcelableExtra(
                             EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -608,7 +617,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -638,7 +647,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -653,12 +662,11 @@
     /**
      * Returns information about the eUICC chip/device.
      *
-     * @return the {@link EuiccInfo}. May be null if {@link #isEnabled()} is false or the eUICC is
-     *     not ready.
+     * @return the {@link EuiccInfo}. May be null if the eUICC is not ready.
      */
     @Nullable
     public EuiccInfo getEuiccInfo() {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             return null;
         }
         try {
@@ -683,7 +691,7 @@
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -704,14 +712,26 @@
      * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
      * intent to prompt the user to accept the download.
      *
+     * <p>On a multi-active SIM device, requires the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+     *  only if the targeted eUICC does not currently have an active subscription or the calling app
+     * is authorized to manage the active subscription on the target eUICC, and the calling app is
+     * authorized to manage any active subscription on any SIM. Without it, an
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+     * intent to prompt the user to accept the download. The caller should also be authorized to
+     * manage the subscription to be enabled.
+     *
      * @param subscriptionId the ID of the subscription to enable. May be
      *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
-     *     current profile without activating another profile to replace it.
+     *     current profile without activating another profile to replace it. If it's a disable
+     *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
+     *     permission, or the calling app must be authorized to manage the active subscription on
+     *     the target eUICC.
      * @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()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -737,7 +757,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -761,7 +781,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -791,7 +811,7 @@
      * @hide
      */
     public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
-        if (!isEnabled()) {
+        if (!refreshCardIdIfInvalid()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -802,6 +822,19 @@
         }
     }
 
+    private boolean refreshCardIdIfInvalid() {
+        if (!isEnabled()) {
+            return false;
+        }
+        // Refresh mCardId if it's invalid.
+        if (mCardId == TelephonyManager.INVALID_CARD_ID) {
+            TelephonyManager tm = (TelephonyManager)
+                    mContext.getSystemService(Context.TELEPHONY_SERVICE);
+            mCardId = tm.getCardIdForDefaultEuicc();
+        }
+        return true;
+    }
+
     private static void sendUnavailableError(PendingIntent callbackIntent) {
         try {
             callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index a49d2d9..6ce9de4 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -220,7 +220,7 @@
      * @hide
      *
      */
-    int setPreferredDataSubscriptionId(int subId);
+    void setPreferredDataSubscriptionId(int subId);
 
     /**
      * Get which subscription is preferred for cellular data.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 927c676..bc43fea 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1843,9 +1843,14 @@
      * @hide
      */
     void switchMultiSimConfig(int numOfSims);
+
     /**
-     * Get how many modems have been activated on the phone
-     * @hide
+     * Get if reboot is required upon altering modems configurations
      */
-    int getNumOfActiveSims();
+    boolean isRebootRequiredForModemConfigChange();
+
+    /**
+     * Get the mapping from logical slots to physical slots.
+     */
+    int[] getSlotsMapping();
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 6567ea7..603c4c2 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -194,6 +194,13 @@
      */
     static final String PROPERTY_MULTI_SIM_CONFIG = "persist.radio.multisim.config";
 
+     /**
+     * Property to indicate if reboot is required when changing modems configurations
+     * Type:  String(true, false) default is false; most devices don't need reboot
+     */
+    String PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE =
+             "persist.radio.reboot_on_modem_change";
+
     /**
      * Property to store default subscription.
      */
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 14a36c8..2016915 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -31,7 +31,7 @@
         String callingPackage, in PendingIntent callbackIntent);
     oneway void getDefaultDownloadableSubscriptionList(int cardId,
         String callingPackage, in PendingIntent callbackIntent);
-    String getEid(int cardId);
+    String getEid(int cardId, String callingPackage);
     int getOtaStatus(int cardId);
     oneway void downloadSubscription(int cardId, in DownloadableSubscription subscription,
         boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle,
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
index 6bac675..6306f0e 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
@@ -173,7 +173,7 @@
             BySelector splitscreenButtonSelector = By.text("Split screen");
             UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
                     FIND_TIMEOUT);
-            assertNotNull("Unable to find Split screen button in Overview", overviewIcon);
+            assertNotNull("Unable to find Split screen button in Overview", splitscreenButton);
             splitscreenButton.click();
         } else {
             // Classic long press recents
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index ca25386..4fd2043 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -14,7 +14,7 @@
             </intent-filter>
         </activity>
         <service android:name="SchedulerService"
-            android:foregroundServiceType="sync">
+                 android:foregroundServiceType="dataSync|mediaPlayback|phoneCall|location|connectedDevice">
         </service>
         <service android:name="TestService" android:process=":test">
         </service>
@@ -23,7 +23,6 @@
         <receiver android:name="Receiver" android:exported="true">
         </receiver>
     </application>
-
     <instrumentation android:name="android.test.InstrumentationTestRunner"
         android:targetPackage="com.android.frameworkperf"
         android:label="Framework Perf Runner"
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index f330b83..1a4ec94 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1027,5 +1027,15 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name="PositionListenerActivity"
+            android:label="RenderNode/PositionListener"
+            android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
new file mode 100644
index 0000000..316aad3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PositionListenerActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+        ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
+        layout.addView(spinner);
+
+        ScrollView scrollingThing = new ScrollView(this);
+        scrollingThing.addView(new MyPositionReporter(this));
+        layout.addView(scrollingThing);
+
+        setContentView(layout);
+    }
+
+    static class MyPositionReporter extends TextView implements RenderNode.PositionUpdateListener {
+        RenderNode mNode;
+        int mCurrentCount = 0;
+        int mTranslateY = 0;
+
+        MyPositionReporter(Context c) {
+            super(c);
+            mNode = new RenderNode("positionListener");
+            mNode.requestPositionUpdates(this);
+            setTextAlignment(TEXT_ALIGNMENT_VIEW_START);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            setMeasuredDimension(getMeasuredWidth(), 10000);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            mNode.setLeftTopRightBottom(left, top, right, bottom);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            ScrollView parent = (ScrollView) getParent();
+            canvas.translate(0, parent.getScrollY());
+            super.onDraw(canvas);
+            canvas.translate(0, -parent.getScrollY());
+            // Inject our listener proxy
+            canvas.drawRenderNode(mNode);
+        }
+
+        @Override
+        public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
+            post(() -> {
+                mCurrentCount++;
+                setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount,
+                        left, top, right, bottom));
+            });
+        }
+
+        @Override
+        public void positionLost(long frameNumber) {
+            post(() -> {
+                mCurrentCount++;
+                setText(mCurrentCount + " No position");
+            });
+        }
+    }
+}
diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk
index 75282ba..89c21b7 100644
--- a/tests/JankBench/Android.mk
+++ b/tests/JankBench/Android.mk
@@ -19,7 +19,7 @@
 
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.design_design \
+    com.google.android.material_material \
     androidx.legacy_legacy-support-v4 \
     androidx.appcompat_appcompat \
     androidx.cardview_cardview \
diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
index 47db602..4de51fb 100644
--- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
+++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
@@ -19,22 +19,24 @@
 import android.content.Intent;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ExpandableListView;
 import android.widget.Toast;
 
-import com.android.benchmark.registry.BenchmarkRegistry;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
 import com.android.benchmark.R;
+import com.android.benchmark.registry.BenchmarkRegistry;
 import com.android.benchmark.results.GlobalResultsStore;
 
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
 import java.io.IOException;
 import java.util.LinkedList;
 import java.util.Queue;
diff --git a/tests/JankBench/app/src/main/res/layout/activity_home.xml b/tests/JankBench/app/src/main/res/layout/activity_home.xml
index fb30747..160d9d7 100644
--- a/tests/JankBench/app/src/main/res/layout/activity_home.xml
+++ b/tests/JankBench/app/src/main/res/layout/activity_home.xml
@@ -23,7 +23,7 @@
     android:fitsSystemWindows="true"
     tools:context=".app.HomeActivity">
 
-    <android.support.design.widget.AppBarLayout
+    <com.google.android.material.appbar.AppBarLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:theme="@style/AppTheme.AppBarOverlay">
@@ -35,7 +35,7 @@
             android:background="?attr/colorPrimary"
             app:popupTheme="@style/AppTheme.PopupOverlay" />
 
-    </android.support.design.widget.AppBarLayout>
+    </com.google.android.material.appbar.AppBarLayout>
 
     <include layout="@layout/content_main" />
 
diff --git a/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
index 3c72dc9..2f4813f 100644
--- a/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
+++ b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
@@ -22,14 +22,14 @@
     android:layout_height="fill_parent"
     android:fitsSystemWindows="true">
 
-    <android.support.design.widget.AppBarLayout
+    <com.google.android.material.appbar.AppBarLayout
         android:id="@+id/appbar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/detail_backdrop_height"
         android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
         android:fitsSystemWindows="true">
 
-        <android.support.design.widget.CollapsingToolbarLayout
+        <com.google.android.material.appbar.CollapsingToolbarLayout
             android:id="@+id/collapsing_toolbar"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -55,9 +55,9 @@
                 android:fitsSystemWindows="true"
                 app:layout_collapseMode="parallax" />
 
-        </android.support.design.widget.CollapsingToolbarLayout>
+        </com.google.android.material.appbar.CollapsingToolbarLayout>
 
-    </android.support.design.widget.AppBarLayout>
+    </com.google.android.material.appbar.AppBarLayout>
 
     <androidx.core.widget.NestedScrollView
         android:layout_width="match_parent"
@@ -78,7 +78,7 @@
 
     </androidx.core.widget.NestedScrollView>
 
-    <android.support.design.widget.FloatingActionButton
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
         android:id="@+id/start_button"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index c1c598d..77cd62e 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -242,8 +242,9 @@
         PackageWatchdog watchdog = createWatchdog();
         long differentVersionCode = 2L;
         TestObserver observer = new TestObserver(OBSERVER_NAME_1) {
-                public int onHealthCheckFailed(String packageName, long versionCode) {
-                    if (versionCode == VERSION_CODE) {
+                @Override
+                public int onHealthCheckFailed(VersionedPackage versionedPackage) {
+                    if (versionedPackage.getVersionCode() == VERSION_CODE) {
                         // Only rollback for specific versionCode
                         return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
                     }
@@ -457,12 +458,12 @@
             mImpact = impact;
         }
 
-        public int onHealthCheckFailed(String packageName, long versionCode) {
+        public int onHealthCheckFailed(VersionedPackage versionedPackage) {
             return mImpact;
         }
 
-        public boolean execute(String packageName, long versionCode) {
-            mFailedPackages.add(packageName);
+        public boolean execute(VersionedPackage versionedPackage) {
+            mFailedPackages.add(versionedPackage.getPackageName());
             return true;
         }
 
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
new file mode 100644
index 0000000..3d57d56
--- /dev/null
+++ b/tests/RollbackTest/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_app {
+    name: "RollbackTestAppAv1",
+    manifest: "TestApp/Av1.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+}
+
+android_app {
+    name: "RollbackTestAppAv2",
+    manifest: "TestApp/Av2.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+}
+
+android_app {
+    name: "RollbackTestAppACrashingV2",
+    manifest: "TestApp/ACrashingV2.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+}
+
+android_app {
+    name: "RollbackTestAppBv1",
+    manifest: "TestApp/Bv1.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+}
+
+android_app {
+    name: "RollbackTestAppBv2",
+    manifest: "TestApp/Bv2.xml",
+    sdk_version: "current",
+    srcs: ["TestApp/src/**/*.java"],
+}
+
+android_test {
+    name: "RollbackTest",
+    srcs: ["src/**/*.java"],
+    static_libs: ["android-support-test"],
+    test_suites: ["general-tests"],
+    java_resources: [
+        ":RollbackTestAppAv1",
+        ":RollbackTestAppAv2",
+        ":RollbackTestAppACrashingV2",
+        ":RollbackTestAppBv1",
+        ":RollbackTestAppBv2",
+    ],
+    test_config: "RollbackTest.xml",
+    sdk_version: "system_current",
+}
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
deleted file mode 100644
index 780bb24..0000000
--- a/tests/RollbackTest/Android.mk
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# RollbackTestAppAv1.apk
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
-LOCAL_MANIFEST_FILE := TestApp/Av1.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppAv1
-include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
-
-# RollbackTestAppAv2.apk
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
-LOCAL_MANIFEST_FILE := TestApp/Av2.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppAv2
-include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
-
-# RollbackTestAppACrashingV2.apk
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
-LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
-include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
-
-# RollbackTestAppBv1.apk
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
-LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppBv1
-include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
-
-# RollbackTestAppBv2.apk
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
-LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppBv2
-include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
-
-# RollbackTest
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := RollbackTest
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_JAVA_RESOURCE_FILES := \
-  $(ROLLBACK_TEST_APP_AV1) \
-  $(ROLLBACK_TEST_APP_AV2) \
-  $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
-  $(ROLLBACK_TEST_APP_BV1) \
-  $(ROLLBACK_TEST_APP_BV2)
-LOCAL_SDK_VERSION := system_current
-LOCAL_TEST_CONFIG := RollbackTest.xml
-include $(BUILD_PACKAGE)
-
-# Clean up local variables
-ROLLBACK_TEST_APP_AV1 :=
-ROLLBACK_TEST_APP_AV2 :=
-ROLLBACK_TEST_APP_A_CRASHING_V2 :=
-ROLLBACK_TEST_APP_BV1 :=
-ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 4b277ae..ace0e6d 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -37,6 +37,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -628,9 +629,12 @@
         assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
     }
 
+    // TODO: Allow installing test app along atomically with module metadata package so that
+    // a failed test app will be flagged as a failed mainline app
     /**
      * Test bad update automatic rollback.
      */
+    @Ignore
     @Test
     public void testBadUpdateRollback() throws Exception {
         BroadcastReceiver crashCountReceiver = null;
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 6ec0fbb..608bf2f 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -15,7 +15,7 @@
 LOCAL_USE_AAPT2 := true
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.design_design \
+    com.google.android.material_material \
     androidx.legacy_legacy-support-v4 \
     androidx.appcompat_appcompat \
     androidx.cardview_cardview \
diff --git a/tests/UiBench/res/layout/activity_navigation_drawer.xml b/tests/UiBench/res/layout/activity_navigation_drawer.xml
index 282fb78..383432a 100644
--- a/tests/UiBench/res/layout/activity_navigation_drawer.xml
+++ b/tests/UiBench/res/layout/activity_navigation_drawer.xml
@@ -27,7 +27,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"/>
 
-    <android.support.design.widget.NavigationView
+    <com.google.android.material.navigation.NavigationView
         android:id="@+id/nav_view"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
diff --git a/tests/UiBench/res/layout/app_bar_navigation_drawer.xml b/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
index b979134..cf209ac 100644
--- a/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
+++ b/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
@@ -21,7 +21,7 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <android.support.design.widget.AppBarLayout
+    <com.google.android.material.appbar.AppBarLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
@@ -31,6 +31,6 @@
             android:layout_width="match_parent"
             android:layout_height="?attr/actionBarSize"
             android:background="?attr/colorPrimary"/>
-    </android.support.design.widget.AppBarLayout>
+    </com.google.android.material.appbar.AppBarLayout>
 
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
index 6e24ce1..2bf6040 100644
--- a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
@@ -16,18 +16,20 @@
 package com.android.test.uibench;
 
 import android.os.Bundle;
-import android.support.design.widget.NavigationView;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.ListFragment;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
 import android.view.MenuItem;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
 
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.ListFragment;
+
+import com.google.android.material.navigation.NavigationView;
+
 public class ClippedListActivity extends AppCompatActivity
         implements NavigationView.OnNavigationItemSelectedListener {
 
diff --git a/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java b/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
index 6a7761ce..7784539 100644
--- a/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
@@ -16,13 +16,15 @@
 package com.android.test.uibench;
 
 import android.os.Bundle;
-import android.support.design.widget.NavigationView;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
+import android.view.MenuItem;
+
 import androidx.appcompat.app.ActionBarDrawerToggle;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
-import android.view.MenuItem;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
+import com.google.android.material.navigation.NavigationView;
 
 public class NavigationDrawerActivity extends AppCompatActivity
         implements NavigationView.OnNavigationItemSelectedListener {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 3127d74..50468cb 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -922,6 +922,7 @@
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mConnected = true;
             mConfig = new VpnConfig();
+            mConfig.isMetered = false;
         }
 
         @Override
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 5b17224..46de3d0 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -168,6 +168,8 @@
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
         when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
+        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
 
         doNothing().when(mNetService).registerObserver(any());
     }
@@ -544,23 +546,28 @@
         final Network wifi = new Network(2);
 
         final Map<Network, NetworkCapabilities> networks = new HashMap<>();
-        networks.put(mobile, new NetworkCapabilities()
-                .addTransportType(TRANSPORT_CELLULAR)
-                .addCapability(NET_CAPABILITY_INTERNET)
-                .addCapability(NET_CAPABILITY_NOT_METERED)
-                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
-                .setLinkDownstreamBandwidthKbps(10));
-        networks.put(wifi, new NetworkCapabilities()
-                .addTransportType(TRANSPORT_WIFI)
-                .addCapability(NET_CAPABILITY_INTERNET)
-                .addCapability(NET_CAPABILITY_NOT_ROAMING)
-                .addCapability(NET_CAPABILITY_NOT_CONGESTED)
-                .setLinkUpstreamBandwidthKbps(20));
+        networks.put(
+                mobile,
+                new NetworkCapabilities()
+                        .addTransportType(TRANSPORT_CELLULAR)
+                        .addCapability(NET_CAPABILITY_INTERNET)
+                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                        .setLinkDownstreamBandwidthKbps(10));
+        networks.put(
+                wifi,
+                new NetworkCapabilities()
+                        .addTransportType(TRANSPORT_WIFI)
+                        .addCapability(NET_CAPABILITY_INTERNET)
+                        .addCapability(NET_CAPABILITY_NOT_METERED)
+                        .addCapability(NET_CAPABILITY_NOT_ROAMING)
+                        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                        .setLinkUpstreamBandwidthKbps(20));
         setMockedNetworks(networks);
 
         final NetworkCapabilities caps = new NetworkCapabilities();
 
-        Vpn.updateCapabilities(mConnectivityManager, new Network[] { }, caps);
+        Vpn.updateCapabilities(
+                mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(caps.hasTransport(TRANSPORT_WIFI));
@@ -570,17 +577,33 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
+        Vpn.updateCapabilities(
+                mConnectivityManager,
+                new Network[] {mobile},
+                caps,
+                false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(caps.hasTransport(TRANSPORT_WIFI));
         assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
         assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
-        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
+        Vpn.updateCapabilities(
+                mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
+        assertTrue(caps.hasTransport(TRANSPORT_VPN));
+        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+        Vpn.updateCapabilities(
+                mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
         assertTrue(caps.hasTransport(TRANSPORT_WIFI));
@@ -590,7 +613,11 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
+        Vpn.updateCapabilities(
+                mConnectivityManager,
+                new Network[] {mobile, wifi},
+                caps,
+                false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
         assertTrue(caps.hasTransport(TRANSPORT_WIFI));
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 0f72229..ec286759 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -16,8 +16,16 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -27,12 +35,22 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.util.SharedLog;
+import android.os.Bundle;
+import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.CarrierConfigManager;
+import android.test.mock.MockContentResolver;
 
 import com.android.internal.R;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.connectivity.MockableSystemProperties;
 
 import org.junit.After;
@@ -42,6 +60,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class EntitlementManagerTest {
@@ -51,7 +73,6 @@
 
     @Mock private CarrierConfigManager mCarrierConfigManager;
     @Mock private Context mContext;
-    @Mock private ContentResolver mContent;
     @Mock private MockableSystemProperties mSystemProperties;
     @Mock private Resources mResources;
     @Mock private SharedLog mLog;
@@ -59,15 +80,49 @@
     // Like so many Android system APIs, these cannot be mocked because it is marked final.
     // We have to use the real versions.
     private final PersistableBundle mCarrierConfig = new PersistableBundle();
+    private final TestLooper mLooper = new TestLooper();
+    private Context mMockContext;
+    private MockContentResolver mContentResolver;
 
-    private EntitlementManager mEnMgr;
+    private TestStateMachine mSM;
+    private WrappedEntitlementManager mEnMgr;
+
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
+    }
+
+    public class WrappedEntitlementManager extends EntitlementManager {
+        public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+        public boolean everRunUiEntitlement = false;
+
+        public WrappedEntitlementManager(Context ctx, StateMachine target,
+                SharedLog log, MockableSystemProperties systemProperties) {
+            super(ctx, target, log, systemProperties);
+        }
+
+        @Override
+        protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
+            everRunUiEntitlement = true;
+            receiver.send(fakeEntitlementResult, null);
+        }
+    }
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getContentResolver()).thenReturn(mContent);
         when(mResources.getStringArray(R.array.config_tether_dhcp_range))
             .thenReturn(new String[0]);
         when(mResources.getStringArray(R.array.config_tether_usb_regexs))
@@ -80,12 +135,21 @@
             .thenReturn(new int[0]);
         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
 
-        mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        mMockContext = new MockContext(mContext);
+        mSM = new TestStateMachine();
+        mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
     }
 
     @After
-    public void tearDown() throws Exception {}
+    public void tearDown() throws Exception {
+        if (mSM != null) {
+            mSM.quit();
+            mSM = null;
+        }
+    }
 
     private void setupForRequiredProvisioning() {
         // Produce some acceptable looking provision app setting if requested.
@@ -104,7 +168,7 @@
     @Test
     public void canRequireProvisioning() {
         setupForRequiredProvisioning();
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
         assertTrue(mEnMgr.isTetherProvisioningRequired());
     }
 
@@ -113,7 +177,7 @@
         setupForRequiredProvisioning();
         when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
             .thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
         // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
         // Therefore provisioning still be required.
         assertTrue(mEnMgr.isTetherProvisioningRequired());
@@ -123,7 +187,7 @@
     public void toleratesCarrierConfigMissing() {
         setupForRequiredProvisioning();
         when(mCarrierConfigManager.getConfig()).thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
         // We still have a provisioning app configured, so still require provisioning.
         assertTrue(mEnMgr.isTetherProvisioningRequired());
     }
@@ -133,12 +197,143 @@
         setupForRequiredProvisioning();
         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
             .thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
         assertFalse(mEnMgr.isTetherProvisioningRequired());
         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
             .thenReturn(new String[] {"malformedApp"});
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
         assertFalse(mEnMgr.isTetherProvisioningRequired());
     }
 
+    @Test
+    public void testGetLastEntitlementCacheValue() throws Exception {
+        final CountDownLatch mCallbacklatch = new CountDownLatch(1);
+        // 1. Entitlement check is not required.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.everRunUiEntitlement = false;
+        ResultReceiver receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+        callbackTimeoutHelper(mCallbacklatch);
+        assertFalse(mEnMgr.everRunUiEntitlement);
+
+        setupForRequiredProvisioning();
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        // 2. No cache value and don't need to run entitlement check.
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+        callbackTimeoutHelper(mCallbacklatch);
+        assertFalse(mEnMgr.everRunUiEntitlement);
+        // 3. No cache value and ui entitlement check is needed.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertTrue(mEnMgr.everRunUiEntitlement);
+        // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+        callbackTimeoutHelper(mCallbacklatch);
+        assertFalse(mEnMgr.everRunUiEntitlement);
+        // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertTrue(mEnMgr.everRunUiEntitlement);
+        // 6. Cache value is TETHER_ERROR_NO_ERROR.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+        callbackTimeoutHelper(mCallbacklatch);
+        assertFalse(mEnMgr.everRunUiEntitlement);
+        // 7. Test get value for other downstream type.
+        mEnMgr.everRunUiEntitlement = false;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementValue(TETHERING_USB, receiver, false);
+        callbackTimeoutHelper(mCallbacklatch);
+        assertFalse(mEnMgr.everRunUiEntitlement);
+    }
+
+    void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
+        if (!latch.await(1, TimeUnit.SECONDS)) {
+            fail("Timout, fail to recieve callback");
+        }
+    }
+    public class TestStateMachine extends StateMachine {
+        public final ArrayList<Message> messages = new ArrayList<>();
+        private final State mLoggingState =
+                new EntitlementManagerTest.TestStateMachine.LoggingState();
+
+        class LoggingState extends State {
+            @Override public void enter() {
+                messages.clear();
+            }
+
+            @Override public void exit() {
+                messages.clear();
+            }
+
+            @Override public boolean processMessage(Message msg) {
+                messages.add(msg);
+                return false;
+            }
+        }
+
+        public TestStateMachine() {
+            super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper());
+            addState(mLoggingState);
+            setInitialState(mLoggingState);
+            super.start();
+        }
+    }
 }
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 3c3edda..672731c 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -301,6 +301,7 @@
                         break;
                     }
                     // This is not alphabetical, so we fall through to variant
+                    [[fallthrough]];
                 case 5:
                 case 6:
                 case 7:
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 8f75287..bc3a9a1 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -108,6 +108,7 @@
         "link/ProductFilter.cpp",
         "link/PrivateAttributeMover.cpp",
         "link/ReferenceLinker.cpp",
+        "link/ResourceExcluder.cpp",
         "link/TableMerger.cpp",
         "link/XmlCompatVersioner.cpp",
         "link/XmlNamespaceRemover.cpp",
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 729447e..8463046 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -59,6 +59,7 @@
 #include "link/ManifestFixer.h"
 #include "link/NoDefaultResourceRemover.h"
 #include "link/ReferenceLinker.h"
+#include "link/ResourceExcluder.h"
 #include "link/TableMerger.h"
 #include "link/XmlCompatVersioner.h"
 #include "optimize/ResourceDeduper.h"
@@ -1828,6 +1829,29 @@
       }
     }
 
+    if (!options_.exclude_configs_.empty()) {
+      std::vector<ConfigDescription> excluded_configs;
+
+      for (auto& config_string : options_.exclude_configs_) {
+        ConfigDescription config_description;
+
+        if (!ConfigDescription::Parse(config_string, &config_description)) {
+          context_->GetDiagnostics()->Error(DiagMessage()
+                                                << "failed to parse --excluded-configs "
+                                                << config_string);
+          return 1;
+        }
+
+        excluded_configs.push_back(config_description);
+      }
+
+      ResourceExcluder excluder(excluded_configs);
+      if (!excluder.Consume(context_, &final_table_)) {
+        context_->GetDiagnostics()->Error(DiagMessage() << "failed excluding configurations");
+        return 1;
+      }
+    }
+
     if (!options_.no_resource_deduping) {
       ResourceDeduper deduper;
       if (!deduper.Consume(context_, &final_table_)) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index f70470a..590a6bb 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -82,6 +82,9 @@
   std::vector<SplitConstraints> split_constraints;
   std::vector<std::string> split_paths;
 
+  // Configurations to exclude
+  std::vector<std::string> exclude_configs_;
+
   // Stable ID options.
   std::unordered_map<ResourceName, ResourceId> stable_id_map;
   Maybe<std::string> resource_id_map_path;
@@ -255,6 +258,9 @@
             "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
             "On Windows, use a semicolon ';' separator instead.",
         &split_args_);
+    AddOptionalFlagList("--exclude-configs",
+        "Excludes values of resources whose configs contain the specified qualifiers.",
+        &options_.exclude_configs_);
     AddOptionalSwitch("--debug-mode",
         "Inserts android:debuggable=\"true\" in to the application node of the\n"
             "manifest, making the application debuggable even on production devices.",
diff --git a/tools/aapt2/link/ResourceExcluder.cpp b/tools/aapt2/link/ResourceExcluder.cpp
new file mode 100644
index 0000000..2555995
--- /dev/null
+++ b/tools/aapt2/link/ResourceExcluder.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open 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 "link/ResourceExcluder.h"
+
+#include <algorithm>
+
+#include "DominatorTree.h"
+#include "ResourceTable.h"
+
+using android::ConfigDescription;
+
+namespace aapt {
+
+namespace {
+
+void RemoveIfExcluded(std::set<std::pair<ConfigDescription, int>>& excluded_configs_,
+                      IAaptContext* context,
+                      ResourceEntry* entry,
+                      ResourceConfigValue* value) {
+  const ConfigDescription& config = value->config;
+
+  // If this entry is a default, ignore
+  if (config == ConfigDescription::DefaultConfig()) {
+    return;
+  }
+
+  for (auto& excluded_pair : excluded_configs_) {
+
+    const ConfigDescription& excluded_config = excluded_pair.first;
+    const int& excluded_diff = excluded_pair.second;
+
+    // Check whether config contains all flags in excluded config
+    int node_diff = config.diff(excluded_config);
+    int masked_diff = excluded_diff & node_diff;
+
+    if (masked_diff == 0) {
+      if (context->IsVerbose()) {
+        context->GetDiagnostics()->Note(
+            DiagMessage(value->value->GetSource())
+                << "excluded resource \""
+                << entry->name
+                << "\" with config "
+                << config.toString());
+      }
+      value->value = {};
+      return;
+    }
+  }
+}
+
+}  // namespace
+
+bool ResourceExcluder::Consume(IAaptContext* context, ResourceTable* table) {
+  for (auto& package : table->packages) {
+    for (auto& type : package->types) {
+      for (auto& entry : type->entries) {
+        for (auto& value : entry->values) {
+          RemoveIfExcluded(excluded_configs_, context, entry.get(), value.get());
+        }
+
+        // Erase the values that were removed.
+        entry->values.erase(
+            std::remove_if(
+                entry->values.begin(), entry->values.end(),
+                [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+                  return val == nullptr || val->value == nullptr;
+                }),
+            entry->values.end());
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/link/ResourceExcluder.h b/tools/aapt2/link/ResourceExcluder.h
new file mode 100644
index 0000000..1890b34
--- /dev/null
+++ b/tools/aapt2/link/ResourceExcluder.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_LINK_RESOURCEEXCLUDER_H
+#define AAPT_LINK_RESOURCEEXCLUDER_H
+
+#include "android-base/macros.h"
+
+#include "process/IResourceTableConsumer.h"
+
+using android::ConfigDescription;
+
+namespace aapt {
+
+// Removes excluded configs from resources.
+class ResourceExcluder : public IResourceTableConsumer {
+ public:
+  explicit ResourceExcluder(std::vector<ConfigDescription>& excluded_configs) {
+    for (auto& config: excluded_configs) {
+      int diff_from_default = config.diff(ConfigDescription::DefaultConfig());
+      excluded_configs_.insert(std::pair(config, diff_from_default));
+    }
+  }
+
+  bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResourceExcluder);
+
+  std::set<std::pair<ConfigDescription, int>> excluded_configs_;
+};
+
+} // namespace aapt
+
+#endif  // AAPT_LINK_RESOURCEEXCLUDER_H
diff --git a/tools/aapt2/link/ResourceExcluder_test.cpp b/tools/aapt2/link/ResourceExcluder_test.cpp
new file mode 100644
index 0000000..c9d01e4
--- /dev/null
+++ b/tools/aapt2/link/ResourceExcluder_test.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2019 The Android Open 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 "link/ResourceExcluder.h"
+
+#include "ResourceTable.h"
+#include "test/Test.h"
+
+using ::aapt::test::HasValue;
+using ::android::ConfigDescription;
+using ::testing::Not;
+
+namespace {
+
+ConfigDescription BuildArg(const std::string arg) {
+  ConfigDescription config_description;
+  ConfigDescription::Parse(arg, &config_description);
+  return config_description;
+}
+
+std::vector<ConfigDescription> BuildArgList(const std::string arg) {
+  ConfigDescription config_description;
+  ConfigDescription::Parse(arg, &config_description);
+  return { config_description };
+}
+
+} // namespace
+
+namespace aapt {
+
+TEST(ResourceExcluderTest, NonMatchConfigNotExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto fr_config = test::ParseConfigOrDie("fr");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .Build();
+
+  auto args = BuildArgList("en");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, HasValue("android:string/test", fr_config));
+}
+
+TEST(ResourceExcluderTest, ExactConfigExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto fr_config = test::ParseConfigOrDie("fr");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .Build();
+
+  auto args = BuildArgList("fr");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_config)));
+}
+
+TEST(ResourceExcluderTest, MoreSpecificConfigExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto fr_land_config = test::ParseConfigOrDie("fr-land");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, fr_land_config, "fr-land")
+          .Build();
+
+  auto args = BuildArgList("fr");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_land_config)));
+}
+
+TEST(ResourceExcluderTest, MultipleMoreSpecificConfigExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto night_config = test::ParseConfigOrDie("night");
+  auto fr_config = test::ParseConfigOrDie("fr");
+  auto fr_land_config = test::ParseConfigOrDie("fr-land");
+  auto fr_night_config = test::ParseConfigOrDie("fr-night");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, night_config, "night")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .AddString("android:string/test", ResourceId{}, fr_land_config, "fr-land")
+          .AddString("android:string/test", ResourceId{}, fr_night_config, "fr-night")
+          .Build();
+
+  auto args = BuildArgList("fr");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, HasValue("android:string/test", night_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_config)));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_land_config)));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_night_config)));
+}
+
+TEST(ResourceExcluderTest, MultipleConfigsExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto night_config = test::ParseConfigOrDie("night");
+  auto fr_config = test::ParseConfigOrDie("fr");
+  auto fr_land_config = test::ParseConfigOrDie("fr-land");
+  auto fr_night_config = test::ParseConfigOrDie("fr-night");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, night_config, "night")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .AddString("android:string/test", ResourceId{}, fr_land_config, "fr-land")
+          .AddString("android:string/test", ResourceId{}, fr_night_config, "fr-night")
+          .Build();
+
+  std::vector<ConfigDescription> args;
+  args.push_back(BuildArg("land"));
+  args.push_back(BuildArg("night"));
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", night_config)));
+  EXPECT_THAT(table, HasValue("android:string/test", fr_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_land_config)));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_night_config)));
+}
+
+TEST(ResourceExcluderTest, LessSpecificConfigNotExcluded) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto fr_config = test::ParseConfigOrDie("fr");
+  auto fr_land_config = test::ParseConfigOrDie("fr-land");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .AddString("android:string/test", ResourceId{}, fr_land_config, "fr-land")
+          .Build();
+
+  auto args = BuildArgList("fr-land");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, HasValue("android:string/test", fr_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_land_config)));
+}
+
+TEST(ResourceExcluderTest, LowerPrecedenceStillExcludes) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto fr_config = test::ParseConfigOrDie("fr");
+  auto fr_night_config = test::ParseConfigOrDie("fr-night");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, fr_config, "fr")
+          .AddString("android:string/test", ResourceId{}, fr_night_config, "fr-night")
+          .Build();
+
+  // "night" is lower precedence than "fr"
+  auto args = BuildArgList("night");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, HasValue("android:string/test", fr_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", fr_night_config)));
+}
+
+TEST(ResourceExcluderTest, OnlyExcludesSpecificTier) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  ConfigDescription default_config;
+  auto mdpi_config = test::ParseConfigOrDie("mdpi");
+  auto hdpi_config = test::ParseConfigOrDie("hdpi");
+  auto xhdpi_config = test::ParseConfigOrDie("xhdpi");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/test", ResourceId{}, default_config, "default")
+          .AddString("android:string/test", ResourceId{}, mdpi_config, "mdpi")
+          .AddString("android:string/test", ResourceId{}, hdpi_config, "hdpi")
+          .AddString("android:string/test", ResourceId{}, xhdpi_config, "xhdpi")
+          .Build();
+
+  auto args = BuildArgList("hdpi");
+
+  ASSERT_TRUE(ResourceExcluder(args).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/test", default_config));
+  EXPECT_THAT(table, HasValue("android:string/test", mdpi_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/test", hdpi_config)));
+  EXPECT_THAT(table, HasValue("android:string/test", xhdpi_config));
+}
+
+}  // namespace aapt